From a33274b0f14aca64e8b9d5d337efac86670249f2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:11:36 +0100 Subject: [PATCH 01/75] Create trip details screen --- src/ROUTES.ts | 4 +++ src/SCREENS.ts | 1 + .../ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 1 + src/pages/Travel/TripDetails.tsx | 36 +++++++++++++++++++ 5 files changed, 43 insertions(+) create mode 100644 src/pages/Travel/TripDetails.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index d8f8b0f911057..f324d4af6d2ed 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1343,6 +1343,10 @@ const ROUTES = { TRAVEL_MY_TRIPS: 'travel', TRAVEL_TCS: 'travel/terms', TRACK_TRAINING_MODAL: 'track-training', + TRAVEL_TRIP_DETAILS: { + route: 'r/:reportID/trip/:transactionID', + getRoute: (reportID: string, transactionID: string) => `r/${reportID}/trip/${transactionID}` as const, + }, ONBOARDING_ROOT: { route: 'onboarding', getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding`, backTo), diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 5fd64b0fc0d03..f6a53da3f1997 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -27,6 +27,7 @@ const SCREENS = { TRAVEL: { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', + TRIP_DETAILS: 'Trip_Details', }, SEARCH: { CENTRAL_PANE: 'Search_Central_Pane', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 9295281755e57..05bec49e9bd9e 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,6 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../pages/TripDetails').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 476711c7c116a..2d1511789c24e 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,6 +1320,7 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, + [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx new file mode 100644 index 0000000000000..8c3d1bbadfc18 --- /dev/null +++ b/src/pages/Travel/TripDetails.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import {NativeModules} from 'react-native'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; + +function TripDetails() { + const {translate} = useLocalize(); + const {canUseSpotnanaTravel} = usePermissions(); + + return ( + + + + + + ); +} + +TripDetails.displayName = 'TripDetails'; + +export default TripDetails; From 15c9ae36fc845f298078a1f28939064158088b7b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:11:49 +0100 Subject: [PATCH 02/75] Add translation for trip support --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index c1067e195985e..ec0e49353bbb0 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2424,6 +2424,7 @@ const translations = { hotel: 'Hotel', car: 'Car', viewTrip: 'View trip', + tripSupport: 'Trip support', viewTripDetails: 'View trip details', trip: 'Trip', trips: 'Trips', diff --git a/src/languages/es.ts b/src/languages/es.ts index f7af1be451395..623afb1ff16ae 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2448,6 +2448,7 @@ const translations = { hotel: 'Hotel', car: 'Auto', viewTrip: 'Ver viaje', + tripSupport: 'Soporte de Viaje', viewTripDetails: 'Ver detalles del viaje', trip: 'Viaje', trips: 'Viajes', From 3ae7f986f7fee31a3f065e2b2be31aece0c3ed49 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 10:16:31 +0100 Subject: [PATCH 03/75] Add undefined type for trip details --- src/libs/Navigation/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5877c9c102181..caa5ff5d1a06b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,6 +1380,7 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; + [SCREENS.TRAVEL.TRIP_DETAILS]: undefined; }; type FullScreenNavigatorParamList = { From d2e1df5cf70c7bf112174a58369352fff890b69e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 20 Nov 2024 12:39:33 +0100 Subject: [PATCH 04/75] Fix navigation config --- src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 05bec49e9bd9e..beb030ccb09d8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,7 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, - [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../pages/TripDetails').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetails').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 2d1511789c24e..317c1e15e04f3 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,7 +1320,7 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, - [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS, + [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS.route, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { From 7394a09389a39b499723e05548285e930ea34b9f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 22 Nov 2024 14:19:35 +0100 Subject: [PATCH 05/75] Link the correct backTo on trip details screen --- src/ROUTES.ts | 2 +- src/components/ReportActionItem/MoneyRequestView.tsx | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f324d4af6d2ed..920e328ebfff3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1345,7 +1345,7 @@ const ROUTES = { TRACK_TRAINING_MODAL: 'track-training', TRAVEL_TRIP_DETAILS: { route: 'r/:reportID/trip/:transactionID', - getRoute: (reportID: string, transactionID: string) => `r/${reportID}/trip/${transactionID}` as const, + getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo), }, ONBOARDING_ROOT: { route: 'onboarding', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 381f01aadd893..1533a7515a98e 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -34,7 +34,6 @@ import ViolationsUtils from '@libs/Violations/ViolationsUtils'; import Navigation from '@navigation/Navigation'; import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground'; import * as IOU from '@userActions/IOU'; -import * as Link from '@userActions/Link'; import * as Transaction from '@userActions/Transaction'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -84,7 +83,6 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const session = useSession(); const {isOffline} = useNetwork(); const {translate, toLocaleDigit} = useLocalize(); - const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const parentReportID = report?.parentReportID ?? '-1'; const policyID = report?.policyID ?? '-1'; const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`); @@ -696,11 +694,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} + onPress={() => + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) + } /> )} {shouldShowAttendees && ( From 2c9d2a8c72caa65b5b28a7f5a2294ae00ea975d5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 22 Nov 2024 16:21:06 +0100 Subject: [PATCH 06/75] Access data and fix getTripIDFromTransactionParentReportID --- src/CONST.ts | 2 +- .../ReportActionItem/MoneyRequestView.tsx | 2 +- src/libs/Navigation/types.ts | 6 ++- src/libs/ReportUtils.ts | 6 +-- src/pages/Travel/TripDetails.tsx | 50 ++++++++++++++++++- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index ee70e3b296689..80b52c65bfcf6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -4427,7 +4427,7 @@ const CONST = { BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page', TRAVEL_DOT_URL: 'https://travel.expensify.com', STAGING_TRAVEL_DOT_URL: 'https://staging.travel.expensify.com', - TRIP_ID_PATH: (tripID: string) => `trips/${tripID}`, + TRIP_ID_PATH: (tripID?: string) => (tripID ? `trips/${tripID}` : undefined), SPOTNANA_TMC_ID: '8e8e7258-1cf3-48c0-9cd1-fe78a6e31eed', STAGING_SPOTNANA_TMC_ID: '7a290c6e-5328-4107-aff6-e48765845b81', SCREEN_READER_STATES: { diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 1533a7515a98e..6bc356dd6f151 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -186,7 +186,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals const shouldShowAttendees = useMemo(() => TransactionUtils.shouldShowAttendees(iouType, policy), [iouType, policy]); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest); - const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction) && !!tripID; const {getViolationsForField} = useViolations(transactionViolations ?? [], isReceiptBeingScanned || !ReportUtils.isPaidGroupPolicy(report)); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index caa5ff5d1a06b..469d20d15bbcb 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,7 +1380,11 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; - [SCREENS.TRAVEL.TRIP_DETAILS]: undefined; + [SCREENS.TRAVEL.TRIP_DETAILS]: { + reportID: string; + transactionID: string; + backTo?: string; + }; }; type FullScreenNavigatorParamList = { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 36095b7d88ec3..72f11f4a3150e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8030,8 +8030,8 @@ function getTripTransactions(tripRoomReportID: string | undefined, reportFieldTo return tripTransactionReportIDs.flatMap((reportID) => reportsTransactions[reportID ?? ''] ?? []); } -function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry | undefined | null): string | undefined { - return getReportOrDraftReport(transactionParentReport?.parentReportID)?.tripData?.tripID; +function getTripIDFromTransactionParentReportID(transactionParentReportID: string | undefined): string | undefined { + return getReportOrDraftReport(transactionParentReportID)?.tripData?.tripID; } /** @@ -8700,7 +8700,7 @@ export { updateReportPreview, temporary_getMoneyRequestOptions, getTripTransactions, - getTripIDFromTransactionParentReport, + getTripIDFromTransactionParentReportID, buildOptimisticInvoiceReport, getInvoiceChatByParticipants, shouldShowMerchantColumn, diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index 8c3d1bbadfc18..dab130bde1aae 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -1,15 +1,41 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {NativeModules} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import type {TravelNavigatorParamList} from '@libs/Navigation/types'; +import * as ReportUtils from '@libs/ReportUtils'; +import * as Link from '@userActions/Link'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; -function TripDetails() { +type TripDetailsProps = StackScreenProps; + +function TripDetails({route}: TripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + + console.log(`transaction = `, transaction); + console.log(`report = `, report); + console.log(`parentReport = `, parentReport); + return ( + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> ); From 610ed48107fc58b2dc04a569aaec214da5ab5ffc Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 12:04:05 +0100 Subject: [PATCH 07/75] Add english translations --- src/languages/en.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index ec0e49353bbb0..e525ac717729f 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2421,9 +2421,34 @@ const translations = { error: 'You must accept the Terms & Conditions for travel to continue', }, flight: 'Flight', + flightDetails: { + layover: 'layover', + takeOff: 'Take-off', + landing: 'Landing', + passenger: 'Passenger', + seat: 'Seat', + class: 'Cabin Class', + recordLocator: 'Record locator', + }, hotel: 'Hotel', + hotelDetails: { + checkIn: 'Check-in', + checkOut: 'Check-out', + roomType: 'Room type', + cancellation: 'Cancellation policy', + confirmation: 'Confirmation number', + }, car: 'Car', + carDetails: { + pickUp: 'Pick-up', + dropOff: 'Drop-off', + driver: 'Driver', + carType: 'Car type', + cancellation: 'Cancellation policy', + confirmation: 'Confirmation number', + }, viewTrip: 'View trip', + modifyTrip: 'Modify trip', tripSupport: 'Trip support', viewTripDetails: 'View trip details', trip: 'Trip', From 644243cc5815c134ed7624000e4103b38f94498f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:33:49 +0100 Subject: [PATCH 08/75] Make reservationType optional in getTripReservationIcon function --- src/libs/TripReservationUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index f2ce5113af812..9fd850ab161b8 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -50,7 +50,7 @@ Onyx.connect({ }, }); -function getTripReservationIcon(reservationType: ReservationType): IconAsset { +function getTripReservationIcon(reservationType?: ReservationType): IconAsset { switch (reservationType) { case CONST.RESERVATION_TYPE.FLIGHT: return Expensicons.Plane; From d5dd2afc853a01393f909472368798583fcc06f4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:33:56 +0100 Subject: [PATCH 09/75] Add arrivalGate and cityName fields to Reservation and ReservationTimeDetails types --- src/types/onyx/Transaction.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 4c7e4facd94da..0183286510f6b 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -236,6 +236,12 @@ type Reservation = { /** Payment type of the reservation */ paymentType?: string; + + /** Arrival gate details */ + arrivalGate?: { + /** Arrival terminal number */ + terminal: string; + }; }; /** Model of trip reservation time details */ @@ -257,6 +263,9 @@ type ReservationTimeDetails = { /** Timezone offset */ timezoneOffset?: string; + + /** City name */ + cityName?: string; }; /** Model of airline company details */ From 6c89f5a3f21c2723f18b6b1cab1fc664275c441b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:13 +0100 Subject: [PATCH 10/75] Enhance getFormattedTransportDate function to support shorter date format --- src/libs/DateUtils.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 2cab87639d2f0..11482aec46b56 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -798,15 +798,19 @@ function getFormattedReservationRangeDate(date1: Date, date2: Date): string { /** * Returns a formatted date of departure. * Dates are formatted as follows: - * 1. When the date refers to the current day: Departs on Sunday, Mar 17 at 8:00 - * 2. When the date refers not to the current day: Departs on Wednesday, Mar 17, 2023 at 8:00 + * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. When shorter is true, the output is: Mar 17, 8:00 AM + * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. When shorter is true, the output is: Mar 17, 2023 8:00 AM */ -function getFormattedTransportDate(date: Date): string { +function getFormattedTransportDate(date: Date, shorter = false): string { const {translateLocal} = Localize; if (isThisYear(date)) { - return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return shorter + ? format(date, 'MMM d, h:mm a') + : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } - return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return shorter + ? format(date, 'MMM d, yyyy, h:mm a') + : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } function doesDateBelongToAPastYear(date: string): boolean { From b47f8a2f28ff5bb59b97ce18a0308976256e10e6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:21 +0100 Subject: [PATCH 11/75] Add FlightTripDetails component to display flight reservation information --- src/pages/Travel/FlightTripDetails.tsx | 132 +++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/pages/Travel/FlightTripDetails.tsx diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx new file mode 100644 index 0000000000000..6f32aeed0b428 --- /dev/null +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type FlightTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + return ( + <> + + + + {transaction?.receipt?.reservationList?.map((reservation) => { + const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); + const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); + const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + + return ( + <> + {translate('travel.flight')} + + {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) + + + + + + + + {!!reservation.route?.number && ( + + + + )} + {!!reservation.route?.class && ( + + + + )} + {!!reservation.confirmations?.at(0)?.value && ( + + + + )} + + + ); + })} + + ); +} + +FlightTripDetails.displayName = 'FlightTripDetails'; + +export default FlightTripDetails; From aeddf58eb767159b5184e6de188c4aefd9b3e152 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 25 Nov 2024 17:34:29 +0100 Subject: [PATCH 12/75] Integrate FlightTripDetails component into TripDetails screen and wrap content in ScrollView --- src/pages/Travel/TripDetails.tsx | 60 ++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index dab130bde1aae..6c924d0957b69 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -7,21 +7,21 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; -import useThemeStyles from '@hooks/useThemeStyles'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import FlightTripDetails from './FlightTripDetails'; type TripDetailsProps = StackScreenProps; function TripDetails({route}: TripDetailsProps) { - const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); @@ -30,11 +30,11 @@ function TripDetails({route}: TripDetailsProps) { const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); - console.log(`transaction = `, transaction); - console.log(`report = `, report); - console.log(`parentReport = `, parentReport); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); + const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; return ( - { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} - /> - { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); - }} - /> + + {reservationType === 'flight' && ( + + )} + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + { + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + }} + /> + ); From ae395901e50f5bf0ba1564941d1aab95a4e4d9d5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sun, 24 Nov 2024 20:39:28 +0100 Subject: [PATCH 13/75] Final touches --- src/components/ApprovalWorkflowSection.tsx | 34 +++++++++++++++++++--- src/components/AttachmentModal.tsx | 4 +-- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/components/ApprovalWorkflowSection.tsx b/src/components/ApprovalWorkflowSection.tsx index fd28595e74360..de6626c5bd5fa 100644 --- a/src/components/ApprovalWorkflowSection.tsx +++ b/src/components/ApprovalWorkflowSection.tsx @@ -1,4 +1,5 @@ -import React, {useCallback, useMemo} from 'react'; +/* eslint-disable react/function-component-definition */ +import React, {useCallback, useMemo, useRef} from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -20,11 +21,36 @@ type ApprovalWorkflowSectionProps = { onPress: () => void; }; -function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { +const Test = () => { const styles = useThemeStyles(); const theme = useTheme(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const {translate, toLocaleOrdinal} = useLocalize(); + + const approverTitle = useCallback( + (index: number) => + approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`, + [approvalWorkflow.approvers.length, toLocaleOrdinal, translate], + ); + + const members = useMemo(() => { + if (approvalWorkflow.isDefault) { + return translate('workspace.common.everyone'); + } + + return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') + .map((m) => m.displayName) + .join(', '); + }, [approvalWorkflow.isDefault, translate]); + + return ; +}; + +function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { + const styles = useThemeStyles(); + const theme = useTheme(); const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {translate, toLocaleOrdinal} = useLocalize(); const approverTitle = useCallback( (index: number) => @@ -40,7 +66,7 @@ function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSe return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') .map((m) => m.displayName) .join(', '); - }, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]); + }, [approvalWorkflow]); return ( Date: Tue, 26 Nov 2024 09:31:52 +0100 Subject: [PATCH 14/75] Fix flight page after design changes --- src/libs/DateUtils.ts | 11 +++++ src/pages/Travel/FlightTripDetails.tsx | 60 +++++++++++++++----------- src/pages/Travel/TripDetails.tsx | 3 ++ 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 11482aec46b56..9bf707acdf466 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -13,6 +13,7 @@ import { formatDistance, getDate, getDay, + intervalToDuration, isAfter, isBefore, isSameDay, @@ -813,6 +814,15 @@ function getFormattedTransportDate(date: Date, shorter = false): string { : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } +/** + * Returns a formatted layover duration in format "2h 30m". + */ +function getFormattedDurationBetweenDates(start: Date, end: Date): string { + const {days, hours, minutes} = intervalToDuration({start, end}); + + return `${days ? `${days}d ` : ''}${hours ? `${hours}h ` : ''}${minutes}m`; +} + function doesDateBelongToAPastYear(date: string): boolean { const transactionYear = new Date(date).getFullYear(); return transactionYear !== new Date().getFullYear(); @@ -897,6 +907,7 @@ const DateUtils = { isCardExpired, getDifferenceInDaysFromNow, isValidDateString, + getFormattedDurationBetweenDates, }; export default DateUtils; diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 6f32aeed0b428..b92c4ad57ccef 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -28,29 +28,26 @@ function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProp return ( <> - - {transaction?.receipt?.reservationList?.map((reservation) => { + {transaction?.receipt?.reservationList?.map((reservation, index) => { const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + const nextFlightStartDate = transaction.receipt?.reservationList?.at(index + 1)?.start.date; + const layover = nextFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(reservation.end.date), new Date(nextFlightStartDate)); + return ( <> - {translate('travel.flight')} + {translate('travel.flight')} {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) @@ -72,26 +69,18 @@ function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProp description={translate('travel.flightDetails.takeOff')} title={startDate} helperText={`${reservation.start.longName} (${reservation.start.shortName})${reservation.arrivalGate?.terminal ? `, ${reservation.arrivalGate?.terminal}` : ''}`} - helperTextStyle={styles.mtn2} + helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> - - - + + {!!reservation.route?.number && ( )} + + {!!layover && ( + <> + + + + )} ); })} diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetails.tsx index 6c924d0957b69..5eaee8d60dc4e 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetails.tsx @@ -11,6 +11,7 @@ import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; import * as Link from '@userActions/Link'; @@ -23,6 +24,7 @@ type TripDetailsProps = StackScreenProps { Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); }} + wrapperStyle={styles.mt3} /> Date: Tue, 26 Nov 2024 09:33:48 +0100 Subject: [PATCH 15/75] Change TripDetails into TripDetailsPage --- .../AppNavigator/ModalStackNavigators/index.tsx | 2 +- .../Travel/{TripDetails.tsx => TripDetailsPage.tsx} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/pages/Travel/{TripDetails.tsx => TripDetailsPage.tsx} (92%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index beb030ccb09d8..543b0b374b8d8 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,7 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, - [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetails').default, + [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, }); const SplitDetailsModalStackNavigator = createModalStackNavigator({ diff --git a/src/pages/Travel/TripDetails.tsx b/src/pages/Travel/TripDetailsPage.tsx similarity index 92% rename from src/pages/Travel/TripDetails.tsx rename to src/pages/Travel/TripDetailsPage.tsx index 5eaee8d60dc4e..0f35bc0c36e1c 100644 --- a/src/pages/Travel/TripDetails.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -20,9 +20,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import FlightTripDetails from './FlightTripDetails'; -type TripDetailsProps = StackScreenProps; +type TripDetailsPageProps = StackScreenProps; -function TripDetails({route}: TripDetailsProps) { +function TripDetailsPage({route}: TripDetailsPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -43,7 +43,7 @@ function TripDetails({route}: TripDetailsProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID={TripDetails.displayName} + testID={TripDetailsPage.displayName} shouldShowOfflineIndicatorInWideScreen > Date: Tue, 26 Nov 2024 10:16:35 +0100 Subject: [PATCH 16/75] Create HotelTripDetails --- src/pages/Travel/HotelTripDetails.tsx | 88 +++++++++++++++++++++++++++ src/pages/Travel/TripDetailsPage.tsx | 7 +++ 2 files changed, 95 insertions(+) create mode 100644 src/pages/Travel/HotelTripDetails.tsx diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx new file mode 100644 index 0000000000000..f0f8ec7398cca --- /dev/null +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type HotelTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + const hotelReservation = transaction?.receipt?.reservationList?.at(0); + + if (!transaction || !hotelReservation) { + return null; + } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); + const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); + const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); + + return ( + <> + + {translate('travel.hotel')} + {hotelReservation.start.longName} + + + + {!!hotelReservation.confirmations?.at(0)?.value && ( + + + + )} + + ); +} + +HotelTripDetails.displayName = 'FlightTripDetails'; + +export default HotelTripDetails; diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 0f35bc0c36e1c..590ab2949ea57 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -19,6 +19,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import FlightTripDetails from './FlightTripDetails'; +import HotelTripDetails from './HotelTripDetails'; type TripDetailsPageProps = StackScreenProps; @@ -61,6 +62,12 @@ function TripDetailsPage({route}: TripDetailsPageProps) { personalDetails={personalDetails} /> )} + {reservationType === 'hotel' && ( + + )} Date: Tue, 26 Nov 2024 10:16:43 +0100 Subject: [PATCH 17/75] Add missing translation --- src/languages/en.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index e525ac717729f..81d2a9dfc5c98 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2432,6 +2432,7 @@ const translations = { }, hotel: 'Hotel', hotelDetails: { + guest: 'Guest', checkIn: 'Check-in', checkOut: 'Check-out', roomType: 'Room type', From 2eb1bb96d8d633b605c75a0d9e059cac1ff4b4f8 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 11:48:40 +0100 Subject: [PATCH 18/75] Revert "Final touches" This reverts commit 04d94db6fc38eb5cd30e85373c3ac58ca28ebc26. --- src/components/ApprovalWorkflowSection.tsx | 34 +++------------------- src/components/AttachmentModal.tsx | 4 ++- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/components/ApprovalWorkflowSection.tsx b/src/components/ApprovalWorkflowSection.tsx index de6626c5bd5fa..fd28595e74360 100644 --- a/src/components/ApprovalWorkflowSection.tsx +++ b/src/components/ApprovalWorkflowSection.tsx @@ -1,5 +1,4 @@ -/* eslint-disable react/function-component-definition */ -import React, {useCallback, useMemo, useRef} from 'react'; +import React, {useCallback, useMemo} from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -21,36 +20,11 @@ type ApprovalWorkflowSectionProps = { onPress: () => void; }; -const Test = () => { - const styles = useThemeStyles(); - const theme = useTheme(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const {translate, toLocaleOrdinal} = useLocalize(); - - const approverTitle = useCallback( - (index: number) => - approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`, - [approvalWorkflow.approvers.length, toLocaleOrdinal, translate], - ); - - const members = useMemo(() => { - if (approvalWorkflow.isDefault) { - return translate('workspace.common.everyone'); - } - - return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') - .map((m) => m.displayName) - .join(', '); - }, [approvalWorkflow.isDefault, translate]); - - return ; -}; - function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) { const styles = useThemeStyles(); const theme = useTheme(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); const {translate, toLocaleOrdinal} = useLocalize(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const approverTitle = useCallback( (index: number) => @@ -66,7 +40,7 @@ function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSe return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName') .map((m) => m.displayName) .join(', '); - }, [approvalWorkflow]); + }, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]); return ( Date: Tue, 26 Nov 2024 11:52:58 +0100 Subject: [PATCH 19/75] Remove flight from hotel page --- src/pages/Travel/HotelTripDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index f0f8ec7398cca..b77b3530fca9d 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -73,7 +73,7 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) {!!hotelReservation.confirmations?.at(0)?.value && ( @@ -83,6 +83,6 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) ); } -HotelTripDetails.displayName = 'FlightTripDetails'; +HotelTripDetails.displayName = 'HotelTripDetails'; export default HotelTripDetails; From a4a0cded4bec256a13dd85c03ffdbf176319e618 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:19 +0100 Subject: [PATCH 20/75] Add a blank line for improved readability in HotelTripDetails component --- src/pages/Travel/HotelTripDetails.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index b77b3530fca9d..32f0a68368287 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,6 +30,7 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) if (!transaction || !hotelReservation) { return null; } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); From 30e27d1c2ba817f810c4f2e4a2b8c57a1346eb48 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:26 +0100 Subject: [PATCH 21/75] Add CarTripDetails component for displaying car rental information --- src/pages/Travel/CarTripDetails.tsx | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/pages/Travel/CarTripDetails.tsx diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx new file mode 100644 index 0000000000000..25c417425a9fc --- /dev/null +++ b/src/pages/Travel/CarTripDetails.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type CarTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; +}; + +function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + const carReservation = transaction?.receipt?.reservationList?.at(0); + + if (!transaction || !carReservation) { + return null; + } + + const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); + const checkInDate = DateUtils.getFormattedTransportDate(new Date(carReservation.start.date), true); + const checkOutDate = DateUtils.getFormattedTransportDate(new Date(carReservation.end.date), true); + + return ( + <> + + {translate('travel.carDetails.rentalCar')} + {carReservation.vendor} + + + + + {!!carReservation.carInfo?.name && ( + + + + )} + {!!carReservation.reservationID && ( + + + + )} + + ); +} + +CarTripDetails.displayName = 'FlightTripDetails'; + +export default CarTripDetails; From a8b78f875075575e7400e32e8137c95bf5b1957b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:37 +0100 Subject: [PATCH 22/75] Refactor getReservationsFromTripTransactions to return structured reservation data and sort by start date --- src/libs/TripReservationUtils.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index 9fd850ab161b8..a8714122da975 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -63,11 +63,19 @@ function getTripReservationIcon(reservationType?: ReservationType): IconAsset { } } -function getReservationsFromTripTransactions(transactions: Transaction[]): Reservation[] { +type ReservationData = {reservation: Reservation; transactionID: string; reportID: string}; + +function getReservationsFromTripTransactions(transactions: Transaction[]): ReservationData[] { return transactions - .map((item) => item?.receipt?.reservationList ?? []) - .filter((item) => item.length > 0) - .flat(); + .flatMap( + (item) => + item?.receipt?.reservationList?.map((reservation) => ({ + reservation, + transactionID: item.transactionID, + reportID: item.reportID, + })) ?? [], + ) + .sort((a, b) => new Date(a.reservation.start.date).getTime() - new Date(b.reservation.start.date).getTime()); } function getTripEReceiptIcon(transaction?: Transaction): IconAsset | undefined { @@ -115,3 +123,4 @@ function bookATrip(translate: LocaleContextProps['translate'], setCtaErrorMessag }); } export {getTripReservationIcon, getReservationsFromTripTransactions, getTripEReceiptIcon, bookATrip}; +export type {ReservationData}; From f4f5501fe0c7afb0d4ee4c819643c40e9ce2ae79 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:42:47 +0100 Subject: [PATCH 23/75] Add translation for rental car --- src/languages/en.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 81d2a9dfc5c98..e1b932a3852ad 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2441,6 +2441,7 @@ const translations = { }, car: 'Car', carDetails: { + rentalCar: 'Rental car', pickUp: 'Pick-up', dropOff: 'Drop-off', driver: 'Driver', From d49cdce25f3839547511714ccaf716499374616d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:01 +0100 Subject: [PATCH 24/75] Fix navigation route in MoneyRequestView to use parentReportID --- src/components/ReportActionItem/MoneyRequestView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 6bc356dd6f151..4fb50ad39cc12 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -695,7 +695,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals title={translate('travel.viewTripDetails')} icon={Expensicons.Suitcase} onPress={() => - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) } /> )} From 092e430260226f367a28150edb89477e8cca435c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:29 +0100 Subject: [PATCH 25/75] Refactor TripDetailsView and TripRoomPreview to use structured reservation data and improve navigation handling --- .../ReportActionItem/TripDetailsView.tsx | 25 +++++++++++---- .../ReportActionItem/TripRoomPreview.tsx | 32 +++++++------------ src/libs/ReportUtils.ts | 6 ++-- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 32cbc5dd853e2..0043232d11198 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -11,11 +11,13 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; +import Navigation from '@libs/Navigation/Navigation'; import variables from '@styles/variables'; import * as Expensicons from '@src/components/Icon/Expensicons'; import CONST from '@src/CONST'; import * as ReportUtils from '@src/libs/ReportUtils'; import * as TripReservationUtils from '@src/libs/TripReservationUtils'; +import ROUTES from '@src/ROUTES'; import type {Reservation, ReservationTimeDetails} from '@src/types/onyx/Transaction'; type TripDetailsViewProps = { @@ -28,9 +30,11 @@ type TripDetailsViewProps = { type ReservationViewProps = { reservation: Reservation; + transactionID: string; + reportID: string; }; -function ReservationView({reservation}: ReservationViewProps) { +function ReservationView({reservation, transactionID, reportID}: ReservationViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -129,6 +133,7 @@ function ReservationView({reservation}: ReservationViewProps) { iconWidth={20} iconStyles={[StyleUtils.getTripReservationIconContainer(false), styles.mr3]} secondaryIconFill={theme.icon} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, Navigation.getReportRHPActiveRoute()))} /> ); } @@ -138,7 +143,7 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai const {translate} = useLocalize(); const tripTransactions = ReportUtils.getTripTransactions(tripRoomReportID); - const reservations: Reservation[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); return ( @@ -153,11 +158,17 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai <> - {reservations.map((reservation) => ( - - - - ))} + {reservationsData.map(({reservation, transactionID, reportID}) => { + return ( + + + + ); + })} ; +const renderItem = ({item}: {item: TripReservationUtils.ReservationData}) => ; function TripRoomPreview({action, chatReportID, containerStyles, contextMenuAnchor, isHovered = false, checkIfContextMenuActive = () => {}}: TripRoomPreviewProps) { const styles = useThemeStyles(); @@ -112,31 +111,22 @@ function TripRoomPreview({action, chatReportID, containerStyles, contextMenuAnch const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReport?.iouReportID}`); - const tripTransactions = ReportUtils.getTripTransactions(chatReport?.iouReportID, 'reportID'); - const reservations: Reservation[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); + const tripTransactions = ReportUtils.getTripTransactions(chatReport?.reportID); + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(tripTransactions); const dateInfo = chatReport?.tripData ? DateUtils.getFormattedDateRange(new Date(chatReport.tripData.startDate), new Date(chatReport.tripData.endDate)) : ''; const {totalDisplaySpend} = ReportUtils.getMoneyRequestSpendBreakdown(chatReport); + const currency = iouReport?.currency ?? chatReport?.currency; const displayAmount = useMemo(() => { if (totalDisplaySpend) { - return CurrencyUtils.convertToDisplayString(totalDisplaySpend, iouReport?.currency); + return CurrencyUtils.convertToDisplayString(totalDisplaySpend, currency); } - // If iouReport is not available, get amount from the action message (Ex: "Domain20821's Workspace owes $33.00" or "paid ₫60" or "paid -₫60 elsewhere") - let displayAmountValue = ''; - const actionMessage = getReportActionText(action) ?? ''; - const splits = actionMessage.split(' '); - - splits.forEach((split) => { - if (!/\d/.test(split)) { - return; - } - - displayAmountValue = split; - }); - - return displayAmountValue; - }, [action, iouReport?.currency, totalDisplaySpend]); + return CurrencyUtils.convertToDisplayString( + tripTransactions.reduce((acc, transaction) => acc + Math.abs(transaction.amount), 0), + currency, + ); + }, [currency, totalDisplaySpend, tripTransactions]); return ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 72f11f4a3150e..534559dd8cb89 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -854,7 +854,7 @@ function isChatReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.CHAT; } -function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boolean { +function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): report is Report { return report?.type === CONST.REPORT.TYPE.INVOICE; } @@ -1659,6 +1659,8 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ +function isMoneyRequestReport(reportOrID: OnyxInputOrEntry): reportOrID is Report; +function isMoneyRequestReport(reportOrID: SearchReport | string): boolean; function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); @@ -2837,7 +2839,7 @@ function hasNonReimbursableTransactions(iouReportID: string | undefined): boolea function getMoneyRequestSpendBreakdown(report: OnyxInputOrEntry, allReportsDict?: OnyxCollection): SpendBreakdown { const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); - let moneyRequestReport; + let moneyRequestReport: OnyxEntry; if (isMoneyRequestReport(report) || isInvoiceReport(report)) { moneyRequestReport = report; } From 6c59ca4a4208e2d946c75be3357c551ecb4fe5a9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 26 Nov 2024 15:43:39 +0100 Subject: [PATCH 26/75] Add CarTripDetails component to TripDetailsPage for displaying car rental information --- src/pages/Travel/TripDetailsPage.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 590ab2949ea57..971128101486c 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -18,6 +18,7 @@ import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import CarTripDetails from './CarTripDetails'; import FlightTripDetails from './FlightTripDetails'; import HotelTripDetails from './HotelTripDetails'; @@ -32,9 +33,8 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); - const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; @@ -68,6 +68,12 @@ function TripDetailsPage({route}: TripDetailsPageProps) { personalDetails={personalDetails} /> )} + {reservationType === 'car' && ( + + )} Date: Wed, 27 Nov 2024 18:29:42 +0100 Subject: [PATCH 27/75] Add Trip Summary screen --- src/ROUTES.ts | 7 +- src/SCREENS.ts | 1 + .../ReportActionItem/TripDetailsView.tsx | 9 ++- src/pages/Travel/TripSummary.tsx | 65 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/pages/Travel/TripSummary.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 920e328ebfff3..2be153e4dad37 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -1343,10 +1343,15 @@ const ROUTES = { TRAVEL_MY_TRIPS: 'travel', TRAVEL_TCS: 'travel/terms', TRACK_TRAINING_MODAL: 'track-training', - TRAVEL_TRIP_DETAILS: { + TRAVEL_TRIP_SUMMARY: { route: 'r/:reportID/trip/:transactionID', getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo), }, + TRAVEL_TRIP_DETAILS: { + route: 'r/:reportID/trip/:transactionID/:reservationIndex', + getRoute: (reportID: string, transactionID: string, reservationIndex: number, backTo?: string) => + getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}/${reservationIndex}`, backTo), + }, ONBOARDING_ROOT: { route: 'onboarding', getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding`, backTo), diff --git a/src/SCREENS.ts b/src/SCREENS.ts index f6a53da3f1997..eeb805d0ca364 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -27,6 +27,7 @@ const SCREENS = { TRAVEL: { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', + TRIP_SUMMARY: 'Travel_TripSummary', TRIP_DETAILS: 'Trip_Details', }, SEARCH: { diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 0043232d11198..dfc282b230e4c 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -32,9 +32,10 @@ type ReservationViewProps = { reservation: Reservation; transactionID: string; reportID: string; + reservationIndex: number; }; -function ReservationView({reservation, transactionID, reportID}: ReservationViewProps) { +function ReservationView({reservation, transactionID, reportID, reservationIndex}: ReservationViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -133,7 +134,7 @@ function ReservationView({reservation, transactionID, reportID}: ReservationView iconWidth={20} iconStyles={[StyleUtils.getTripReservationIconContainer(false), styles.mr3]} secondaryIconFill={theme.icon} - onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, Navigation.getReportRHPActiveRoute()))} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, reservationIndex, Navigation.getReportRHPActiveRoute()))} /> ); } @@ -158,13 +159,14 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai <> - {reservationsData.map(({reservation, transactionID, reportID}) => { + {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { return ( ); @@ -181,3 +183,4 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai TripDetailsView.displayName = 'TripDetailsView'; export default TripDetailsView; +export {ReservationView}; diff --git a/src/pages/Travel/TripSummary.tsx b/src/pages/Travel/TripSummary.tsx new file mode 100644 index 0000000000000..a024d28bd6db0 --- /dev/null +++ b/src/pages/Travel/TripSummary.tsx @@ -0,0 +1,65 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React from 'react'; +import {NativeModules} from 'react-native'; +import {useOnyx} from 'react-native-onyx'; +import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import {ReservationView} from '@components/ReportActionItem/TripDetailsView'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; +import type {TravelNavigatorParamList} from '@libs/Navigation/types'; +import * as TripReservationUtils from '@src/libs/TripReservationUtils'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; + +type TripDetailsPageProps = StackScreenProps; + +function TripDetailsPage({route}: TripDetailsPageProps) { + const {translate} = useLocalize(); + const {canUseSpotnanaTravel} = usePermissions(); + + const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); + const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; + const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(transaction ? [transaction] : []); + + return ( + + + + + {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { + return ( + + + + ); + })} + + + + ); +} + +TripDetailsPage.displayName = 'TripDetailsPage'; + +export default TripDetailsPage; From 9c5963e50f51d541e70f171454947e9e440c6d22 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:30:02 +0100 Subject: [PATCH 28/75] Add address translation to common --- src/languages/en.ts | 3 ++- src/languages/es.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e1b932a3852ad..f745d2d3f5798 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -474,6 +474,7 @@ const translations = { links: 'Links', days: 'days', rename: 'Rename', + address: 'Address', }, location: { useCurrent: 'Use current location', @@ -2441,7 +2442,7 @@ const translations = { }, car: 'Car', carDetails: { - rentalCar: 'Rental car', + rentalCar: 'Car rental', pickUp: 'Pick-up', dropOff: 'Drop-off', driver: 'Driver', diff --git a/src/languages/es.ts b/src/languages/es.ts index 623afb1ff16ae..edcba33bd58a4 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -465,6 +465,7 @@ const translations = { sent: 'Enviado', links: 'Enlaces', days: 'días', + address: 'Dirección', }, connectionComplete: { title: 'Conexión completa', From 34553ef3aeb1a09e42257e9703f15f1b463c2cf3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:30:50 +0100 Subject: [PATCH 29/75] Add screen to navigator and fix types --- .../AppNavigator/ModalStackNavigators/index.tsx | 1 + src/libs/Navigation/types.ts | 6 ++++++ .../Travel/{TripSummary.tsx => TripSummaryPage.tsx} | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) rename src/pages/Travel/{TripSummary.tsx => TripSummaryPage.tsx} (90%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 543b0b374b8d8..377013af0adab 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -105,6 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.TRAVEL.MY_TRIPS]: () => require('../../../../pages/Travel/MyTripsPage').default, [SCREENS.TRAVEL.TCS]: () => require('../../../../pages/Travel/TravelTerms').default, + [SCREENS.TRAVEL.TRIP_SUMMARY]: () => require('../../../../pages/Travel/TripSummaryPage').default, [SCREENS.TRAVEL.TRIP_DETAILS]: () => require('../../../../pages/Travel/TripDetailsPage').default, }); diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 469d20d15bbcb..750542a551724 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1380,9 +1380,15 @@ type RightModalNavigatorParamList = { type TravelNavigatorParamList = { [SCREENS.TRAVEL.MY_TRIPS]: undefined; + [SCREENS.TRAVEL.TRIP_SUMMARY]: { + reportID: string; + transactionID: string; + backTo?: string; + }; [SCREENS.TRAVEL.TRIP_DETAILS]: { reportID: string; transactionID: string; + reservationIndex: number; backTo?: string; }; }; diff --git a/src/pages/Travel/TripSummary.tsx b/src/pages/Travel/TripSummaryPage.tsx similarity index 90% rename from src/pages/Travel/TripSummary.tsx rename to src/pages/Travel/TripSummaryPage.tsx index a024d28bd6db0..92a9aab8e1074 100644 --- a/src/pages/Travel/TripSummary.tsx +++ b/src/pages/Travel/TripSummaryPage.tsx @@ -15,9 +15,9 @@ import * as TripReservationUtils from '@src/libs/TripReservationUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -type TripDetailsPageProps = StackScreenProps; +type TripSummaryPageProps = StackScreenProps; -function TripDetailsPage({route}: TripDetailsPageProps) { +function TripSummaryPage({route}: TripSummaryPageProps) { const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); @@ -30,7 +30,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { includeSafeAreaPaddingBottom={false} shouldEnablePickerAvoiding={false} shouldEnableMaxHeight - testID={TripDetailsPage.displayName} + testID={TripSummaryPage.displayName} shouldShowOfflineIndicatorInWideScreen > Date: Wed, 27 Nov 2024 18:32:54 +0100 Subject: [PATCH 30/75] Navigate to summary conditionally --- .../ReportActionItem/MoneyRequestView.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 4fb50ad39cc12..56fdf582f5b76 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -694,9 +694,17 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals - Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())) - } + onPress={() => { + const reservations = transaction?.receipt?.reservationList?.length ?? 0; + if (reservations > 1) { + Navigation.navigate( + ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute()), + ); + } + Navigation.navigate( + ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', 0, Navigation.getReportRHPActiveRoute()), + ); + }} /> )} {shouldShowAttendees && ( From a1ebfe7db4c327d6d9d8ff755976fb526ab0ac1f Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:13 +0100 Subject: [PATCH 31/75] Clean date utils around transport --- src/libs/DateUtils.ts | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 9bf707acdf466..21c0a2b7e3752 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -799,19 +799,34 @@ function getFormattedReservationRangeDate(date1: Date, date2: Date): string { /** * Returns a formatted date of departure. * Dates are formatted as follows: - * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. When shorter is true, the output is: Mar 17, 8:00 AM - * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. When shorter is true, the output is: Mar 17, 2023 8:00 AM + * 1. When the date refers to the current year: Departs on Sunday, Mar 17 at 8:00. + * 2. When the date refers not to the current year: Departs on Wednesday, Mar 17, 2023 at 8:00. */ -function getFormattedTransportDate(date: Date, shorter = false): string { +function getFormattedTransportDate(date: Date): string { const {translateLocal} = Localize; if (isThisYear(date)) { - return shorter - ? format(date, 'MMM d, h:mm a') - : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; } - return shorter - ? format(date, 'MMM d, yyyy, h:mm a') - : `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; + return `${translateLocal('travel.departs')} ${format(date, 'EEEE, MMM d, yyyy')} ${translateLocal('common.conjunctionAt')} ${format(date, 'HH:MM')}`; +} + +/** + * Returns a formatted flight date and hour. + * Dates are formatted as follows: + * 1. When the date refers to the current year: Wednesday, Mar 17 8:00 AM + * 2. When the date refers not to the current year: Wednesday, Mar 17, 2023 8:00 AM + */ +function getFormattedTransportDateAndHour(date: Date): {date: string; hour: string} { + if (isThisYear(date)) { + return { + date: format(date, 'EEEE, MMM d'), + hour: format(date, 'h:mm a'), + }; + } + return { + date: format(date, 'EEEE, MMM d, yyyy'), + hour: format(date, 'h:mm a'), + }; } /** @@ -903,6 +918,7 @@ const DateUtils = { getFormattedDateRange, getFormattedReservationRangeDate, getFormattedTransportDate, + getFormattedTransportDateAndHour, doesDateBelongToAPastYear, isCardExpired, getDifferenceInDaysFromNow, From aa0fb9403d8c8d67ec0dc4800e859c6fe01f9610 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:36 +0100 Subject: [PATCH 32/75] Add reservation index to display just one flight --- src/libs/TripReservationUtils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libs/TripReservationUtils.ts b/src/libs/TripReservationUtils.ts index a8714122da975..6ddf0dc30f57a 100644 --- a/src/libs/TripReservationUtils.ts +++ b/src/libs/TripReservationUtils.ts @@ -63,16 +63,17 @@ function getTripReservationIcon(reservationType?: ReservationType): IconAsset { } } -type ReservationData = {reservation: Reservation; transactionID: string; reportID: string}; +type ReservationData = {reservation: Reservation; transactionID: string; reportID: string; reservationIndex: number}; function getReservationsFromTripTransactions(transactions: Transaction[]): ReservationData[] { return transactions .flatMap( (item) => - item?.receipt?.reservationList?.map((reservation) => ({ + item?.receipt?.reservationList?.map((reservation, reservationIndex) => ({ reservation, transactionID: item.transactionID, reportID: item.reportID, + reservationIndex, })) ?? [], ) .sort((a, b) => new Date(a.reservation.start.date).getTime() - new Date(b.reservation.start.date).getTime()); From 1d541a1e95271c6682c74a7ad9f55c23363952f4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:33:48 +0100 Subject: [PATCH 33/75] Update config configuration --- src/libs/Navigation/linkingConfig/config.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 317c1e15e04f3..2cf2f28a08e73 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1320,7 +1320,13 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, [SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS, - [SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS.route, + [SCREENS.TRAVEL.TRIP_SUMMARY]: ROUTES.TRAVEL_TRIP_SUMMARY.route, + [SCREENS.TRAVEL.TRIP_DETAILS]: { + path: ROUTES.TRAVEL_TRIP_DETAILS.route, + parse: { + reservationIndex: (reservationIndex: string) => parseInt(reservationIndex, 10), + }, + }, }, }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { From fe182384b590466d33be942542788135551a16d1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:34:03 +0100 Subject: [PATCH 34/75] Update styles after design changes --- src/pages/Travel/CarTripDetails.tsx | 19 ++- src/pages/Travel/FlightTripDetails.tsx | 171 ++++++++++++------------- src/pages/Travel/HotelTripDetails.tsx | 24 ++-- src/pages/Travel/TripDetailsPage.tsx | 3 +- 4 files changed, 108 insertions(+), 109 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 25c417425a9fc..ceff885c349bc 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -4,7 +4,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -32,8 +31,8 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { } const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); - const checkInDate = DateUtils.getFormattedTransportDate(new Date(carReservation.start.date), true); - const checkOutDate = DateUtils.getFormattedTransportDate(new Date(carReservation.end.date), true); + const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); + const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); return ( <> @@ -46,12 +45,10 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { interactive={false} wrapperStyle={styles.pb3} /> - {translate('travel.carDetails.rentalCar')} - {carReservation.vendor} ; personalDetails: OnyxEntry; + reservationIndex: number; }; -function FlightTripDetails({transaction, personalDetails}: FlightTripDetailsProps) { +function FlightTripDetails({transaction, personalDetails, reservationIndex}: FlightTripDetailsProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); + const flightReservation = transaction?.receipt?.reservationList?.at(reservationIndex); + + if (!flightReservation) { + return null; + } + + const reservationIcon = TripReservationUtils.getTripReservationIcon(flightReservation.type); + const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.start.date)); + const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.end.date)); + + const prevFlightStartDate = reservationIndex > 0 && transaction?.receipt?.reservationList?.at(reservationIndex - 1)?.end.date; + const layover = prevFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(prevFlightStartDate), new Date(flightReservation.start.date)); + return ( <> - {transaction?.receipt?.reservationList?.map((reservation, index) => { - const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); - const startDate = DateUtils.getFormattedTransportDate(new Date(reservation.start.date), true); - const endDate = DateUtils.getFormattedTransportDate(new Date(reservation.end.date), true); + {!!layover && ( + <> + + + + )} - const nextFlightStartDate = transaction.receipt?.reservationList?.at(index + 1)?.start.date; - const layover = nextFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(reservation.end.date), new Date(nextFlightStartDate)); + + + - return ( - <> - {translate('travel.flight')} - - {reservation.start.cityName} ({reservation.start.shortName}) {translate('common.conjunctionTo')} {reservation.end.cityName} ({reservation.end.shortName}) - - + {!!flightReservation.route?.number && ( + + + + )} + {!!flightReservation.route?.class && ( + + + )} + {!!flightReservation.confirmations?.at(0)?.value && ( + - - - {!!reservation.route?.number && ( - - - - )} - {!!reservation.route?.class && ( - - - - )} - {!!reservation.confirmations?.at(0)?.value && ( - - - - )} - - - {!!layover && ( - <> - - - - )} - - ); - })} + + )} + ); } diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 32f0a68368287..4ce3fbabb7adc 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -4,7 +4,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -32,8 +31,8 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) } const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); - const checkInDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.start.date), true); - const checkOutDate = DateUtils.getFormattedTransportDate(new Date(hotelReservation.end.date), true); + const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); + const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); return ( <> @@ -46,12 +45,10 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) interactive={false} wrapperStyle={styles.pb3} /> - {translate('travel.hotel')} - {hotelReservation.start.longName} + {!!hotelReservation.confirmations?.at(0)?.value && ( diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 971128101486c..96d7ec2b56ba1 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -52,7 +52,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { shouldShow={!canUseSpotnanaTravel && !NativeModules.HybridAppModule} > @@ -60,6 +60,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { )} {reservationType === 'hotel' && ( From c882db81eb5712c02fb0b01a652163b651a87e2d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:52:46 +0100 Subject: [PATCH 35/75] Rename TRIP_DETAILS screen identifier to maintain consistent naming convention --- src/SCREENS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 2efe8084b3bb6..5b708e5371477 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -28,7 +28,7 @@ const SCREENS = { MY_TRIPS: 'Travel_MyTrips', TCS: 'Travel_TCS', TRIP_SUMMARY: 'Travel_TripSummary', - TRIP_DETAILS: 'Trip_Details', + TRIP_DETAILS: 'Travel_TripDetails', }, SEARCH: { CENTRAL_PANE: 'Search_Central_Pane', From c20e09e6810ddc7f4979d5fc8e72d3116fdff964 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 18:54:41 +0100 Subject: [PATCH 36/75] Fix typecheck and lint --- src/libs/ReportUtils.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 16d1d65d99f13..c647223a47156 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -860,7 +860,7 @@ function isChatReport(report: OnyxEntry): boolean { return report?.type === CONST.REPORT.TYPE.CHAT; } -function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): report is Report { +function isInvoiceReport(report: OnyxInputOrEntry | SearchReport): boolean { return report?.type === CONST.REPORT.TYPE.INVOICE; } @@ -1667,8 +1667,6 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { /** * Checks if a report is an IOU or expense report. */ -function isMoneyRequestReport(reportOrID: OnyxInputOrEntry): reportOrID is Report; -function isMoneyRequestReport(reportOrID: SearchReport | string): boolean; function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { const report = typeof reportOrID === 'string' ? ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportOrID}`] ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); @@ -2848,7 +2846,7 @@ function hasNonReimbursableTransactions(iouReportID: string | undefined): boolea function getMoneyRequestSpendBreakdown(report: OnyxInputOrEntry, allReportsDict?: OnyxCollection): SpendBreakdown { const allAvailableReports = allReportsDict ?? ReportConnection.getAllReports(); let moneyRequestReport: OnyxEntry; - if (isMoneyRequestReport(report) || isInvoiceReport(report)) { + if (report && (isMoneyRequestReport(report) || isInvoiceReport(report))) { moneyRequestReport = report; } if (allAvailableReports && report?.iouReportID) { From 7dcfbd0159a50dba5fd7a7a4f559f844a5d8bcb6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 27 Nov 2024 19:01:16 +0100 Subject: [PATCH 37/75] Add Spanish translations for flight, hotel, and car details --- src/languages/es.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 1897e10ccaac7..3710c3080c99b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2449,9 +2449,36 @@ const translations = { error: 'Debes aceptar los Términos y condiciones para que el viaje continúe', }, flight: 'Vuelo', + flightDetails: { + layover: 'Escala', + takeOff: 'Despegue', + landing: 'Aterrizaje', + passenger: 'Pasajero', + seat: 'Asiento', + class: 'Clase de cabina', + recordLocator: 'Localizador de la reserva', + }, hotel: 'Hotel', + hotelDetails: { + guest: 'Cliente', + checkIn: 'Entrada', + checkOut: 'Salida', + roomType: 'Tipo de habitación', + cancellation: 'Política de cancelación', + confirmation: 'Número de confirmación', + }, car: 'Auto', + carDetails: { + rentalCar: 'Coche de alquiler', + pickUp: 'Recogida', + dropOff: 'Devolución', + driver: 'Conductor', + carType: 'Tipo de coche', + cancellation: 'Política de cancelación', + confirmation: 'Número de confirmación', + }, viewTrip: 'Ver viaje', + modifyTrip: 'Modificar viaje', tripSupport: 'Soporte de Viaje', viewTripDetails: 'Ver detalles del viaje', trip: 'Viaje', From 5ba408aa59533d7f32c22e48d69bc1340b8cff2a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 28 Nov 2024 14:52:46 +0100 Subject: [PATCH 38/75] Refactor reservation type checks to use constants --- src/pages/Travel/TripDetailsPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 96d7ec2b56ba1..ee73282992194 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -56,20 +56,20 @@ function TripDetailsPage({route}: TripDetailsPageProps) { shouldShowBackButton /> - {reservationType === 'flight' && ( + {reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( )} - {reservationType === 'hotel' && ( + {reservationType === CONST.RESERVATION_TYPE.HOTEL && ( )} - {reservationType === 'car' && ( + {reservationType === CONST.RESERVATION_TYPE.CAR && ( Date: Thu, 28 Nov 2024 15:45:44 +0100 Subject: [PATCH 39/75] Add cancellation deadline and policy to car and hotel trip details --- src/languages/en.ts | 2 ++ src/pages/Travel/CarTripDetails.tsx | 16 ++++++++++++++-- src/pages/Travel/HotelTripDetails.tsx | 14 +++++++++++++- src/types/onyx/Transaction.ts | 6 ++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 148f3463495fc..753b4600e78df 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2441,6 +2441,7 @@ const translations = { checkOut: 'Check-out', roomType: 'Room type', cancellation: 'Cancellation policy', + cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, car: 'Car', @@ -2451,6 +2452,7 @@ const translations = { driver: 'Driver', carType: 'Car type', cancellation: 'Cancellation policy', + cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, viewTrip: 'View trip', diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index ceff885c349bc..5e87b0f5b79f0 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -33,6 +33,9 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); + const cancellationText = carReservation.deadline + ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.deadline)).date}` + : carReservation.cancellationPolicy; return ( <> @@ -74,7 +77,7 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { /> {!!carReservation.carInfo?.name && ( - + )} + {!!cancellationText && ( + + + + )} {!!carReservation.reservationID && ( - + @@ -73,8 +76,17 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) title={checkOutDate.hour} interactive={false} /> + {!!cancellationText && ( + + + + )} {!!hotelReservation.confirmations?.at(0)?.value && ( - + Date: Fri, 29 Nov 2024 13:51:28 +0100 Subject: [PATCH 40/75] Rename reservation deadline property and update related components to use the new name --- src/libs/DateUtils.ts | 8 ++++++-- src/pages/Travel/CarTripDetails.tsx | 4 ++-- src/pages/Travel/HotelTripDetails.tsx | 5 +++-- src/types/onyx/Transaction.ts | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 21c0a2b7e3752..8bdbdd2d21d09 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -832,10 +832,14 @@ function getFormattedTransportDateAndHour(date: Date): {date: string; hour: stri /** * Returns a formatted layover duration in format "2h 30m". */ -function getFormattedDurationBetweenDates(start: Date, end: Date): string { +function getFormattedDurationBetweenDates(start: Date, end: Date): string | undefined { const {days, hours, minutes} = intervalToDuration({start, end}); - return `${days ? `${days}d ` : ''}${hours ? `${hours}h ` : ''}${minutes}m`; + if (days && days > 0) { + return; + } + + return `${hours ? `${hours}h ` : ''}${minutes}m`; } function doesDateBelongToAPastYear(date: string): boolean { diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 5e87b0f5b79f0..4ad406e9b8689 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -33,8 +33,8 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); - const cancellationText = carReservation.deadline - ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.deadline)).date}` + const cancellationText = carReservation.cancellationDeadline + ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.cancellationDeadline)).date}` : carReservation.cancellationPolicy; return ( diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 6e3e5e8c8bfb6..f0fa279fbbe2f 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,11 +30,12 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) return null; } + const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); - const cancellationText = hotelReservation.deadline - ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.deadline)).date}` + const cancellationText = hotelReservation.cancellationDeadline + ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.cancellationDeadline)).date}` : hotelReservation.cancellationPolicy; return ( diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 703769dfabf7a..991b5230440f2 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -211,7 +211,7 @@ type Reservation = { cancellationPolicy?: string; /** In car and hotel reservations, this represents the cancellation deadline */ - deadline?: string; + cancellationDeadline?: string; /** Collection of passenger confirmations */ confirmations?: ReservationConfirmation[]; From f906103b84cadcd8612120cbe0fefb592af54afd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:30:47 +0100 Subject: [PATCH 41/75] Rename displayName for CarTripDetails component --- src/pages/Travel/CarTripDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 4ad406e9b8689..711e2b07332c7 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -107,6 +107,6 @@ function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { ); } -CarTripDetails.displayName = 'FlightTripDetails'; +CarTripDetails.displayName = 'CarTripDetails'; export default CarTripDetails; From 79b0cbd84970de02ffeddc99d328729bd55fc81a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:35:31 +0100 Subject: [PATCH 42/75] Add Spanish translation for cancellationUntil in booking and car sections --- src/languages/es.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 9a8a6c69ebe39..c25a9a0f60270 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2474,6 +2474,7 @@ const translations = { checkOut: 'Salida', roomType: 'Tipo de habitación', cancellation: 'Política de cancelación', + cancellationUntil: 'Cancelación gratuita hasta el', confirmation: 'Número de confirmación', }, car: 'Auto', @@ -2484,6 +2485,7 @@ const translations = { driver: 'Conductor', carType: 'Tipo de coche', cancellation: 'Política de cancelación', + cancellationUntil: 'Cancelación gratuita hasta el', confirmation: 'Número de confirmación', }, viewTrip: 'Ver viaje', From 7bdea21dc5f71150e9e4c16b916b571dd796b896 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 29 Nov 2024 14:35:39 +0100 Subject: [PATCH 43/75] Remove unnecessary blank line in HotelTripDetails component --- src/pages/Travel/HotelTripDetails.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index f0fa279fbbe2f..f0b16b0cb142d 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -30,7 +30,6 @@ function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) return null; } - const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); From 889416c1855f1bf2498daf7ea2d33effecda25fb Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 11:37:56 +0100 Subject: [PATCH 44/75] Add optional fields for rail reservations in Transaction --- src/types/onyx/Transaction.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 33a9e2a71141c..3130a9a765606 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -232,6 +232,9 @@ type Reservation = { /** Passenger seat number */ number: string; + + /** Rail route name */ + name?: string; }; /** In car reservations, this represents the car dealership name */ @@ -248,6 +251,12 @@ type Reservation = { /** Arrival terminal number */ terminal: string; }; + + /** Coach number for rail */ + coachNumber?: string; + + /** Seat number for rail */ + seatNumber?: string; }; /** Model of trip reservation time details */ From c8557b09f092535edaf78b23b5f1a91fb2e232dc Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 11:38:03 +0100 Subject: [PATCH 45/75] Add TRAIN constant to CONST for transportation options --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 64040d8c775b5..94e8f98816c90 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5928,6 +5928,7 @@ const CONST = { CAR: 'car', HOTEL: 'hotel', FLIGHT: 'flight', + TRAIN: 'train', }, DOT_SEPARATOR: '•', From 9f7ca7e1dcad8d8b41668022b07f75aaa68f83e5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 11:38:11 +0100 Subject: [PATCH 46/75] Add train translations and details to language file --- src/languages/en.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e39a91709dc8e..b2f2d064ee156 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2435,10 +2435,10 @@ const translations = { }, flight: 'Flight', flightDetails: { + passenger: 'Passenger', layover: 'layover', takeOff: 'Take-off', landing: 'Landing', - passenger: 'Passenger', seat: 'Seat', class: 'Cabin Class', recordLocator: 'Record locator', @@ -2464,6 +2464,16 @@ const translations = { cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, + train: 'Train', + trainDetails: { + passenger: 'Passenger', + departs: 'Departs', + arrives: 'Arrives', + coachNumber: 'Coach number', + seat: 'Seat', + fareDetails: 'Fare details', + confirmation: 'Confirmation number', + }, viewTrip: 'View trip', modifyTrip: 'Modify trip', tripSupport: 'Trip support', From 347714992d9d4ec941f65823e1ac0abed8fe89e2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 11:38:20 +0100 Subject: [PATCH 47/75] Add TrainTripDetails component to display train reservation details --- src/pages/Travel/TrainTripDetails.tsx | 114 ++++++++++++++++++++++++++ src/pages/Travel/TripDetailsPage.tsx | 8 ++ 2 files changed, 122 insertions(+) create mode 100644 src/pages/Travel/TrainTripDetails.tsx diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx new file mode 100644 index 0000000000000..afaac7fdbf8ef --- /dev/null +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import DateUtils from '@libs/DateUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; +import CONST from '@src/CONST'; +import type {PersonalDetails, Transaction} from '@src/types/onyx'; + +type TrainTripDetailsProps = { + transaction: OnyxEntry; + personalDetails: OnyxEntry; + reservationIndex: number; +}; + +function TrainTripDetails({transaction, personalDetails, reservationIndex}: TrainTripDetailsProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + + const trainReservation = transaction?.receipt?.reservationList?.at(reservationIndex); + + if (!trainReservation) { + return null; + } + + const reservationIcon = TripReservationUtils.getTripReservationIcon(trainReservation.type); + const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(trainReservation.start.date)); + const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(trainReservation.end.date)); + + return ( + <> + + + + + + + + {!!trainReservation.coachNumber && ( + + + + )} + {!!trainReservation.seatNumber && ( + + + + )} + + + {!!trainReservation.confirmations?.at(0)?.value && ( + + )} + + ); +} + +TrainTripDetails.displayName = 'TrainTripDetails'; + +export default TrainTripDetails; diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index ee73282992194..0b80f8b049d1e 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -21,6 +21,7 @@ import type SCREENS from '@src/SCREENS'; import CarTripDetails from './CarTripDetails'; import FlightTripDetails from './FlightTripDetails'; import HotelTripDetails from './HotelTripDetails'; +import TrainTripDetails from './TrainTripDetails'; type TripDetailsPageProps = StackScreenProps; @@ -75,6 +76,13 @@ function TripDetailsPage({route}: TripDetailsPageProps) { personalDetails={personalDetails} /> )} + {reservationType === CONST.RESERVATION_TYPE.TRAIN && ( + + )} Date: Mon, 2 Dec 2024 12:01:58 +0100 Subject: [PATCH 48/75] Update Spanish translations for train terminology --- src/languages/en.ts | 2 +- src/languages/es.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index b2f2d064ee156..187bf58eaae34 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2464,7 +2464,7 @@ const translations = { cancellationUntil: 'Free cancellation until', confirmation: 'Confirmation number', }, - train: 'Train', + train: 'Rail', trainDetails: { passenger: 'Passenger', departs: 'Departs', diff --git a/src/languages/es.ts b/src/languages/es.ts index c25a9a0f60270..6669c54667ec8 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2459,10 +2459,10 @@ const translations = { }, flight: 'Vuelo', flightDetails: { + passenger: 'Pasajero', layover: 'Escala', takeOff: 'Despegue', landing: 'Aterrizaje', - passenger: 'Pasajero', seat: 'Asiento', class: 'Clase de cabina', recordLocator: 'Localizador de la reserva', @@ -2488,6 +2488,16 @@ const translations = { cancellationUntil: 'Cancelación gratuita hasta el', confirmation: 'Número de confirmación', }, + train: 'Tren', + trainDetails: { + passenger: 'Pasajero', + departs: 'Sale', + arrives: 'Llega', + coachNumber: 'Número de vagón', + seat: 'Asiento', + fareDetails: 'Detalles de la tarifa', + confirmation: 'Número de confirmación', + }, viewTrip: 'Ver viaje', modifyTrip: 'Modificar viaje', tripSupport: 'Soporte de Viaje', From 575918c44f41866efaab33cb7e6c989fcc759671 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 12:07:09 +0100 Subject: [PATCH 49/75] Add train icon SVG and import it into Expensicons --- assets/images/train.svg | 3 +++ src/components/Icon/Expensicons.ts | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 assets/images/train.svg diff --git a/assets/images/train.svg b/assets/images/train.svg new file mode 100644 index 0000000000000..67f70e2964e37 --- /dev/null +++ b/assets/images/train.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index bd4bb64da0509..96f4f503f6b57 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -185,6 +185,7 @@ import Task from '@assets/images/task.svg'; import Thread from '@assets/images/thread.svg'; import ThreeDots from '@assets/images/three-dots.svg'; import ThumbsUp from '@assets/images/thumbs-up.svg'; +import Train from '@assets/images/train.svg'; import Transfer from '@assets/images/transfer.svg'; import Trashcan from '@assets/images/trashcan.svg'; import Unlock from '@assets/images/unlock.svg'; @@ -410,4 +411,5 @@ export { Star, QBDSquare, GalleryNotFound, + Train, }; From 542749b2817714fbed2b520a7297b8067df9a5cd Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 12:18:09 +0100 Subject: [PATCH 50/75] Update train SVG to remove fixed width and height attributes --- assets/images/train.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/images/train.svg b/assets/images/train.svg index 67f70e2964e37..40d8c9d1af8ac 100644 --- a/assets/images/train.svg +++ b/assets/images/train.svg @@ -1,3 +1,3 @@ - - + + From f9b5bbc3b96e53b51d6c529a515093f6e74a4528 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 2 Dec 2024 12:26:29 +0100 Subject: [PATCH 51/75] Add support for train in TripDetailsView and TripRoomPreview components --- .../ReportActionItem/TripDetailsView.tsx | 5 +++- .../ReportActionItem/TripRoomPreview.tsx | 28 +++++++++++-------- src/libs/TripReservationUtils.ts | 2 ++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index dfc282b230e4c..4fcd1906afc96 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -80,11 +80,14 @@ function ReservationView({reservation, transactionID, reportID, reservationIndex const vendor = reservation.vendor ? `${reservation.vendor} • ` : ''; return `${vendor}${reservation.start.location}`; } + if (reservation.type === CONST.RESERVATION_TYPE.TRAIN) { + return reservation.route?.name; + } return reservation.start.address ?? reservation.start.location; }, [reservation]); const titleComponent = () => { - if (reservation.type === CONST.RESERVATION_TYPE.FLIGHT) { + if (reservation.type === CONST.RESERVATION_TYPE.FLIGHT || reservation.type === CONST.RESERVATION_TYPE.TRAIN) { return ( diff --git a/src/components/ReportActionItem/TripRoomPreview.tsx b/src/components/ReportActionItem/TripRoomPreview.tsx index 0299486a22c6b..f8537e4beacf9 100644 --- a/src/components/ReportActionItem/TripRoomPreview.tsx +++ b/src/components/ReportActionItem/TripRoomPreview.tsx @@ -62,26 +62,32 @@ function ReservationView({reservation}: ReservationViewProps) { const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation.type); const title = reservation.type === CONST.RESERVATION_TYPE.CAR ? reservation.carInfo?.name : reservation.start.longName; - const titleComponent = - reservation.type === CONST.RESERVATION_TYPE.FLIGHT ? ( + let titleComponent = ( + + {title} + + ); + + if (reservation.type === CONST.RESERVATION_TYPE.FLIGHT || reservation.type === CONST.RESERVATION_TYPE.TRAIN) { + const startName = reservation.type === CONST.RESERVATION_TYPE.FLIGHT ? reservation.start.shortName : reservation.start.longName; + const endName = reservation.type === CONST.RESERVATION_TYPE.FLIGHT ? reservation.end.shortName : reservation.end.longName; + + titleComponent = ( - {reservation.start.shortName} + {startName} - {reservation.end.shortName} + {endName} - ) : ( - - {title} - ); + } return ( Date: Tue, 3 Dec 2024 09:48:03 +0100 Subject: [PATCH 52/75] Add trip details translation for English and Spanish; update TripSummaryPage title --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/pages/Travel/TripSummaryPage.tsx | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 187bf58eaae34..a43e6ce694b95 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2477,6 +2477,7 @@ const translations = { viewTrip: 'View trip', modifyTrip: 'Modify trip', tripSupport: 'Trip support', + tripDetails: 'Trip details', viewTripDetails: 'View trip details', trip: 'Trip', trips: 'Trips', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6669c54667ec8..c6c377bf83961 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2501,6 +2501,7 @@ const translations = { viewTrip: 'Ver viaje', modifyTrip: 'Modificar viaje', tripSupport: 'Soporte de Viaje', + tripDetails: 'Detalles del viaje', viewTripDetails: 'Ver detalles del viaje', trip: 'Viaje', trips: 'Viajes', diff --git a/src/pages/Travel/TripSummaryPage.tsx b/src/pages/Travel/TripSummaryPage.tsx index 92a9aab8e1074..caf5d471c84c0 100644 --- a/src/pages/Travel/TripSummaryPage.tsx +++ b/src/pages/Travel/TripSummaryPage.tsx @@ -22,7 +22,6 @@ function TripSummaryPage({route}: TripSummaryPageProps) { const {canUseSpotnanaTravel} = usePermissions(); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); - const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; const reservationsData: TripReservationUtils.ReservationData[] = TripReservationUtils.getReservationsFromTripTransactions(transaction ? [transaction] : []); return ( @@ -38,7 +37,7 @@ function TripSummaryPage({route}: TripSummaryPageProps) { shouldShow={!canUseSpotnanaTravel && !NativeModules.HybridAppModule} > From 0ab84cdfa30ebb568a5508c43c2a02769d88b1fb Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 11:03:06 +0100 Subject: [PATCH 53/75] Add customizable icon dimensions and styles to HeaderWithBackButton component --- src/components/HeaderWithBackButton/index.tsx | 10 +++++++--- src/components/HeaderWithBackButton/types.ts | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 2c07c48d52b70..6c956891c86f4 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -26,6 +26,9 @@ import type HeaderWithBackButtonProps from './types'; function HeaderWithBackButton({ icon, iconFill, + iconWidth, + iconHeight, + iconStyles, guidesCallTaskID = '', onBackButtonPress = () => Navigation.goBack(), onCloseButtonPress = () => Navigation.dismissModal(), @@ -180,9 +183,10 @@ function HeaderWithBackButton({ {!!icon && ( )} {!!policyAvatar && ( diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 6eef2b072eee1..68493ef7fa790 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -38,6 +38,15 @@ type HeaderWithBackButtonProps = Partial & { * */ icon?: IconAsset; + /** Icon Width */ + iconWidth?: number; + + /** Icon Height */ + iconHeight?: number; + + /** Any additional styles to pass to the icon container. */ + iconStyles?: StyleProp; + /** Method to trigger when pressing download button of the header */ onDownloadButtonPress?: () => void; From 13dd939a96440042d9724e5bda189fa602735ca3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 11:03:17 +0100 Subject: [PATCH 54/75] Add dynamic reservation icon support in TripDetailsPage component --- src/pages/Travel/TripDetailsPage.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 0b80f8b049d1e..c8172b6ace855 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -10,10 +10,12 @@ import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import type {TravelNavigatorParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; +import * as TripReservationUtils from '@libs/TripReservationUtils'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -28,6 +30,7 @@ type TripDetailsPageProps = StackScreenProps data?.[accountID]}); - const reservationType = transaction?.receipt?.reservationList?.at(0)?.type; + const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; + const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); + const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); return ( {reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( From 0a33fd42f361c634ce71b6c218ebaee15077a72a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 11:11:08 +0100 Subject: [PATCH 55/75] Refactor TripDetailsPage to use reservation object for details rendering --- src/pages/Travel/TripDetailsPage.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index c8172b6ace855..fd117ce8b7907 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -67,30 +67,28 @@ function TripDetailsPage({route}: TripDetailsPageProps) { iconFill={theme.icon} /> - {reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( + {!!reservation && reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( )} - {reservationType === CONST.RESERVATION_TYPE.HOTEL && ( + {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && ( )} - {reservationType === CONST.RESERVATION_TYPE.CAR && ( + {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && ( )} - {reservationType === CONST.RESERVATION_TYPE.TRAIN && ( + {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && ( )} Date: Tue, 3 Dec 2024 11:11:16 +0100 Subject: [PATCH 56/75] Refactor CarTripDetails component to use reservation object for rendering details --- src/pages/Travel/CarTripDetails.tsx | 124 ++++++++++++---------------- 1 file changed, 51 insertions(+), 73 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 711e2b07332c7..920fa73d61407 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -1,108 +1,86 @@ import React from 'react'; -import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; -import * as TripReservationUtils from '@libs/TripReservationUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails, Transaction} from '@src/types/onyx'; +import type {PersonalDetails} from '@src/types/onyx'; +import type {Reservation} from '@src/types/onyx/Transaction'; type CarTripDetailsProps = { - transaction: OnyxEntry; + reservation: Reservation; personalDetails: OnyxEntry; }; -function CarTripDetails({transaction, personalDetails}: CarTripDetailsProps) { +function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { const styles = useThemeStyles(); - const theme = useTheme(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const carReservation = transaction?.receipt?.reservationList?.at(0); - - if (!transaction || !carReservation) { - return null; - } - - const reservationIcon = TripReservationUtils.getTripReservationIcon(carReservation.type); - const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.start.date)); - const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.end.date)); - const cancellationText = carReservation.cancellationDeadline - ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(carReservation.cancellationDeadline)).date}` - : carReservation.cancellationPolicy; + const pickUpDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.start.date)); + const dropOffDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.end.date)); + const cancellationText = reservation.cancellationDeadline + ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(reservation.cancellationDeadline)).date}` + : reservation.cancellationPolicy; return ( <> - - + {reservation.vendor} + {pickUpDate.date} {CONST.DOT_SEPARATOR} {pickUpDate.hour} + + } interactive={false} - helperText={carReservation.start.location} + helperText={reservation.start.location} helperTextStyle={[styles.pb3, styles.mtn2]} /> + {dropOffDate.date} {CONST.DOT_SEPARATOR} {dropOffDate.hour} + + } interactive={false} - helperText={carReservation.end.location} + helperText={reservation.end.location} helperTextStyle={[styles.pb3, styles.mtn2]} /> - - {!!carReservation.carInfo?.name && ( - - - + {!!reservation.carInfo?.name && ( + )} {!!cancellationText && ( - - - + )} - {!!carReservation.reservationID && ( - - - + {!!reservation.reservationID && ( + )} + ); } From 3de4fb934c4054424e30d2453abd71d8bf18f024 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 11:29:38 +0100 Subject: [PATCH 57/75] Add shouldUseHeadlineHeader prop to multiple pages for consistent header styling --- src/components/HeaderWithBackButton/index.tsx | 10 ++++------ src/components/HeaderWithBackButton/types.ts | 3 +++ src/pages/TeachersUnite/SaveTheWorldPage.tsx | 1 + src/pages/settings/AboutPage/AboutPage.tsx | 1 + src/pages/settings/Preferences/PreferencesPage.tsx | 1 + src/pages/settings/Profile/ProfilePage.tsx | 1 + src/pages/settings/Security/SecuritySettingsPage.tsx | 1 + .../settings/Subscription/SubscriptionSettingsPage.tsx | 1 + src/pages/settings/Troubleshoot/TroubleshootPage.tsx | 1 + src/pages/settings/Wallet/WalletPage/WalletPage.tsx | 1 + src/pages/workspace/WorkspaceMoreFeaturesPage.tsx | 1 + src/pages/workspace/WorkspacesListPage.tsx | 2 ++ .../workspace/accounting/PolicyAccountingPage.tsx | 1 + .../workspace/categories/WorkspaceCategoriesPage.tsx | 1 + .../distanceRates/PolicyDistanceRatesPage.tsx | 1 + .../expensifyCard/WorkspaceExpensifyCardListPage.tsx | 1 + src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx | 1 + .../reportFields/WorkspaceReportFieldsPage.tsx | 1 + src/pages/workspace/tags/WorkspaceTagsPage.tsx | 1 + src/pages/workspace/taxes/WorkspaceTaxesPage.tsx | 1 + 20 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 6c956891c86f4..d0907b2ea6782 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -49,6 +49,7 @@ function HeaderWithBackButton({ shouldSetModalVisibility = true, shouldShowThreeDotsButton = false, shouldDisableThreeDotsButton = false, + shouldUseHeadlineHeader = false, stepCounter, subtitle = '', title = '', @@ -77,9 +78,6 @@ function HeaderWithBackButton({ const {translate} = useLocalize(); const {isKeyboardShown} = useKeyboardState(); - // If the icon is present, the header bar should be taller and use different font. - const isCentralPaneSettings = !!icon; - const middleContent = useMemo(() => { if (progressBarPercentage) { return ( @@ -111,14 +109,14 @@ function HeaderWithBackButton({
); }, [ StyleUtils, subTitleLink, - isCentralPaneSettings, + shouldUseHeadlineHeader, policy, progressBarPercentage, report, @@ -143,7 +141,7 @@ function HeaderWithBackButton({ dataSet={{dragArea: false}} style={[ styles.headerBar, - isCentralPaneSettings && styles.headerBarDesktopHeight, + shouldUseHeadlineHeader && styles.headerBarDesktopHeight, shouldShowBorderBottom && styles.borderBottom, // progressBarPercentage can be 0 which would // be falsey, hence using !== undefined explicitly diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 68493ef7fa790..d2d4ba9e4e0f3 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -128,6 +128,9 @@ type HeaderWithBackButtonProps = Partial & { /** Whether we should navigate to report page when the route have a topMostReport */ shouldNavigateToTopMostReport?: boolean; + /** Whether the header should use the headline header style */ + shouldUseHeadlineHeader?: boolean; + /** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'. */ iconFill?: string; diff --git a/src/pages/TeachersUnite/SaveTheWorldPage.tsx b/src/pages/TeachersUnite/SaveTheWorldPage.tsx index b30fedab530e1..f52d20ad90142 100644 --- a/src/pages/TeachersUnite/SaveTheWorldPage.tsx +++ b/src/pages/TeachersUnite/SaveTheWorldPage.tsx @@ -59,6 +59,7 @@ function SaveTheWorldPage() { shouldDisplaySearchRouter onBackButtonPress={() => Navigation.goBack()} icon={Illustrations.TeachersUnite} + shouldUseHeadlineHeader /> diff --git a/src/pages/settings/AboutPage/AboutPage.tsx b/src/pages/settings/AboutPage/AboutPage.tsx index 4232e08f70f05..3ab54c9f763f5 100644 --- a/src/pages/settings/AboutPage/AboutPage.tsx +++ b/src/pages/settings/AboutPage/AboutPage.tsx @@ -138,6 +138,7 @@ function AboutPage() { shouldDisplaySearchRouter onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS)} icon={Illustrations.PalmTree} + shouldUseHeadlineHeader /> diff --git a/src/pages/settings/Preferences/PreferencesPage.tsx b/src/pages/settings/Preferences/PreferencesPage.tsx index 9a7f1872b79ab..48cb8c99cf21e 100755 --- a/src/pages/settings/Preferences/PreferencesPage.tsx +++ b/src/pages/settings/Preferences/PreferencesPage.tsx @@ -44,6 +44,7 @@ function PreferencesPage() { Navigation.goBack()} diff --git a/src/pages/settings/Profile/ProfilePage.tsx b/src/pages/settings/Profile/ProfilePage.tsx index 1ea20a4488204..f12dacfea24a2 100755 --- a/src/pages/settings/Profile/ProfilePage.tsx +++ b/src/pages/settings/Profile/ProfilePage.tsx @@ -123,6 +123,7 @@ function ProfilePage() { shouldShowBackButton={shouldUseNarrowLayout} shouldDisplaySearchRouter icon={Illustrations.Profile} + shouldUseHeadlineHeader /> Navigation.goBack()} icon={Illustrations.LockClosed} + shouldUseHeadlineHeader shouldDisplaySearchRouter /> diff --git a/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx b/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx index c34db3fa77a8f..6e10d41af635e 100644 --- a/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx +++ b/src/pages/settings/Subscription/SubscriptionSettingsPage.tsx @@ -49,6 +49,7 @@ function SubscriptionSettingsPage() { shouldShowBackButton={shouldUseNarrowLayout} shouldDisplaySearchRouter icon={Illustrations.CreditCardsNew} + shouldUseHeadlineHeader /> diff --git a/src/pages/settings/Troubleshoot/TroubleshootPage.tsx b/src/pages/settings/Troubleshoot/TroubleshootPage.tsx index bd0ce596c733c..c9802cf17d18d 100644 --- a/src/pages/settings/Troubleshoot/TroubleshootPage.tsx +++ b/src/pages/settings/Troubleshoot/TroubleshootPage.tsx @@ -108,6 +108,7 @@ function TroubleshootPage() { shouldDisplaySearchRouter onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS)} icon={Illustrations.Lightbulb} + shouldUseHeadlineHeader /> {isLoading && } diff --git a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx index 6b402e562d3a5..1c2068592a866 100644 --- a/src/pages/settings/Wallet/WalletPage/WalletPage.tsx +++ b/src/pages/settings/Wallet/WalletPage/WalletPage.tsx @@ -349,6 +349,7 @@ function WalletPage({shouldListenForResize = false}: WalletPageProps) { title={translate('common.wallet')} onBackButtonPress={() => Navigation.goBack()} icon={Illustrations.MoneyIntoWallet} + shouldUseHeadlineHeader shouldShowBackButton={shouldUseNarrowLayout} shouldDisplaySearchRouter /> diff --git a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx index aa540f63599e5..ddcb89064c7eb 100644 --- a/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx +++ b/src/pages/workspace/WorkspaceMoreFeaturesPage.tsx @@ -420,6 +420,7 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro > diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 46f83a49b9f0a..a6d0861f7d0e8 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -395,6 +395,7 @@ function WorkspacesListPage() { shouldDisplaySearchRouter onBackButtonPress={() => Navigation.goBack()} icon={Illustrations.BigRocket} + shouldUseHeadlineHeader /> @@ -429,6 +430,7 @@ function WorkspacesListPage() { shouldDisplaySearchRouter onBackButtonPress={() => Navigation.goBack()} icon={Illustrations.BigRocket} + shouldUseHeadlineHeader > {!shouldUseNarrowLayout && getHeaderButton()} diff --git a/src/pages/workspace/accounting/PolicyAccountingPage.tsx b/src/pages/workspace/accounting/PolicyAccountingPage.tsx index 7aef46167d5d7..079f6019868c7 100644 --- a/src/pages/workspace/accounting/PolicyAccountingPage.tsx +++ b/src/pages/workspace/accounting/PolicyAccountingPage.tsx @@ -474,6 +474,7 @@ function PolicyAccountingPage({policy}: PolicyAccountingPageProps) { title={translate('workspace.common.accounting')} shouldShowBackButton={shouldUseNarrowLayout} icon={Illustrations.Accounting} + shouldUseHeadlineHeader threeDotsAnchorPosition={styles.threeDotsPopoverOffsetNoCloseButton(windowWidth)} /> diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 56ba8a5440b87..8364427560eb9 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -359,6 +359,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { shouldShowBackButton={shouldUseNarrowLayout} title={selectionModeHeader ? translate('common.selectMultiple') : translate('workspace.common.categories')} icon={!selectionModeHeader ? Illustrations.FolderOpen : undefined} + shouldUseHeadlineHeader={!selectionModeHeader} onBackButtonPress={() => { if (selectionMode?.isEnabled) { setSelectedCategories({}); diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index 8520fd641c50e..19878036030b0 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -295,6 +295,7 @@ function PolicyDistanceRatesPage({ > { diff --git a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx index 200d84724bae9..44e00c05c5a89 100644 --- a/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx +++ b/src/pages/workspace/expensifyCard/WorkspaceExpensifyCardListPage.tsx @@ -119,6 +119,7 @@ function WorkspaceExpensifyCardListPage({route, cardsList}: WorkspaceExpensifyCa > Navigation.goBack()} diff --git a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx index 33ef0109a7a72..0753ba4772f6e 100644 --- a/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx +++ b/src/pages/workspace/perDiem/WorkspacePerDiemPage.tsx @@ -353,6 +353,7 @@ function WorkspacePerDiemPage({route}: WorkspacePerDiemPageProps) { shouldShowBackButton={shouldUseNarrowLayout} title={translate(selectionModeHeader ? 'common.selectMultiple' : 'workspace.common.perDiem')} icon={!selectionModeHeader ? Illustrations.PerDiem : undefined} + shouldUseHeadlineHeader={!selectionModeHeader} onBackButtonPress={() => { if (selectionMode?.isEnabled) { setSelectedPerDiem([]); diff --git a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx index f162f267a44b0..ea29d41199ec6 100644 --- a/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx +++ b/src/pages/workspace/reportFields/WorkspaceReportFieldsPage.tsx @@ -246,6 +246,7 @@ function WorkspaceReportFieldsPage({ > { diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index d5c72048f8a4c..61bd2e3aa42f2 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -364,6 +364,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { > { diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx index 9dbe739ae1dbe..e064c04878a13 100644 --- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx +++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx @@ -297,6 +297,7 @@ function WorkspaceTaxesPage({ > { From a3d68f4f7040ba386112869ef775cef6c71edbc5 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 11:42:58 +0100 Subject: [PATCH 58/75] Refactor HotelTripDetails component to use reservation object for rendering details --- src/pages/Travel/HotelTripDetails.tsx | 107 +++++++++++--------------- src/types/onyx/Transaction.ts | 3 + 2 files changed, 47 insertions(+), 63 deletions(-) diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index f0b16b0cb142d..2f65a4e4cd3b8 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -1,99 +1,80 @@ import React from 'react'; -import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; -import * as TripReservationUtils from '@libs/TripReservationUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails, Transaction} from '@src/types/onyx'; +import type {PersonalDetails} from '@src/types/onyx'; +import type {Reservation} from '@src/types/onyx/Transaction'; type HotelTripDetailsProps = { - transaction: OnyxEntry; + reservation: Reservation; personalDetails: OnyxEntry; }; -function HotelTripDetails({transaction, personalDetails}: HotelTripDetailsProps) { +function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) { const styles = useThemeStyles(); - const theme = useTheme(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const hotelReservation = transaction?.receipt?.reservationList?.at(0); - - if (!transaction || !hotelReservation) { - return null; - } - - const reservationIcon = TripReservationUtils.getTripReservationIcon(hotelReservation.type); - const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.start.date)); - const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.end.date)); - const cancellationText = hotelReservation.cancellationDeadline - ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(hotelReservation.cancellationDeadline)).date}` - : hotelReservation.cancellationPolicy; + const checkInDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.start.date)); + const checkOutDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.end.date)); + const cancellationText = reservation.cancellationDeadline + ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(reservation.cancellationDeadline)).date}` + : reservation.cancellationPolicy; return ( <> - - + {reservation.start.longName} {checkInDate.date}} interactive={false} /> {checkOutDate.date}} interactive={false} /> + + {!!reservation.roomClass && ( + + )} {!!cancellationText && ( - - - + )} - {!!hotelReservation.confirmations?.at(0)?.value && ( - - - + {!!reservation.confirmations?.at(0)?.value && ( + )} + ); } diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 3130a9a765606..e3937fee3c333 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -222,6 +222,9 @@ type Reservation = { /** In hotel reservations, this represents the number of rooms reserved */ numberOfRooms?: number; + /** In hotel reservations, this represents the room class */ + roomClass?: string; + /** In flight reservations, this represents the details of the route */ route?: { /** Route airline code */ From b9d0da2e8504eb012e62be06df315acdfa0bb4a4 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 13:07:43 +0100 Subject: [PATCH 59/75] Add flight layover translation support and abbreviations in English and Spanish --- src/languages/en.ts | 5 ++++- src/languages/es.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index a43e6ce694b95..c99a00465c73d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -68,6 +68,7 @@ import type { FeatureNameParams, FileLimitParams, FiltersAmountBetweenParams, + FlightLayoverParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, @@ -477,6 +478,8 @@ const translations = { days: 'days', rename: 'Rename', address: 'Address', + hourAbbreviation: 'h', + minuteAbbreviation: 'm', chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `Need something specific? Chat with your account manager, ${accountManagerDisplayName}.`, chatNow: 'Chat now', }, @@ -2436,7 +2439,7 @@ const translations = { flight: 'Flight', flightDetails: { passenger: 'Passenger', - layover: 'layover', + layover: ({layover}: FlightLayoverParams) => `You have a
${layover} layover before this flight`, takeOff: 'Take-off', landing: 'Landing', seat: 'Seat', diff --git a/src/languages/es.ts b/src/languages/es.ts index c6c377bf83961..0f23940c1c8b7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -67,6 +67,7 @@ import type { FeatureNameParams, FileLimitParams, FiltersAmountBetweenParams, + FlightLayoverParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, @@ -468,6 +469,8 @@ const translations = { links: 'Enlaces', days: 'días', address: 'Dirección', + hourAbbreviation: 'h', + minuteAbbreviation: 'm', chatWithAccountManager: ({accountManagerDisplayName}: ChatWithAccountManagerParams) => `¿Necesitas algo específico? Habla con tu gerente de cuenta, ${accountManagerDisplayName}.`, chatNow: 'Chatear ahora', }, @@ -2460,7 +2463,7 @@ const translations = { flight: 'Vuelo', flightDetails: { passenger: 'Pasajero', - layover: 'Escala', + layover: ({layover}: FlightLayoverParams) => `Tienes una escala de ${layover} antes de este vuelo`, takeOff: 'Despegue', landing: 'Aterrizaje', seat: 'Asiento', From 977804917a0a1bb85ff022ca8e778afb2559ae50 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 13:07:49 +0100 Subject: [PATCH 60/75] Add FlightLayoverParams type for layover details --- src/languages/params.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/languages/params.ts b/src/languages/params.ts index 90b789efae58a..8c62fcb81cfe9 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -567,6 +567,10 @@ type ChatWithAccountManagerParams = { accountManagerDisplayName: string; }; +type FlightLayoverParams = { + layover: string; +}; + export type { AuthenticationErrorParams, ImportMembersSuccessfullDescriptionParams, @@ -771,4 +775,5 @@ export type { CurrencyCodeParams, CompanyNameParams, ChatWithAccountManagerParams, + FlightLayoverParams, }; From 5eac23a9e088f5cd0a00d8ab5939b6ab7dbd2aaf Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 13:08:06 +0100 Subject: [PATCH 61/75] Enhance duration formatting functions to support translation for hours and minutes --- src/libs/DateUtils.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libs/DateUtils.ts b/src/libs/DateUtils.ts index 8bdbdd2d21d09..5ea3fd605d6ff 100644 --- a/src/libs/DateUtils.ts +++ b/src/libs/DateUtils.ts @@ -37,6 +37,7 @@ import {es} from 'date-fns/locale/es'; import throttle from 'lodash/throttle'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; +import type {LocaleContextProps} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {timezoneBackwardMap} from '@src/TIMEZONES'; @@ -832,14 +833,21 @@ function getFormattedTransportDateAndHour(date: Date): {date: string; hour: stri /** * Returns a formatted layover duration in format "2h 30m". */ -function getFormattedDurationBetweenDates(start: Date, end: Date): string | undefined { +function getFormattedDurationBetweenDates(translate: LocaleContextProps['translate'], start: Date, end: Date): string | undefined { const {days, hours, minutes} = intervalToDuration({start, end}); if (days && days > 0) { return; } - return `${hours ? `${hours}h ` : ''}${minutes}m`; + return `${hours ? `${hours}${translate('common.hourAbbreviation')} ` : ''}${minutes}${translate('common.minuteAbbreviation')}`; +} + +function getFormattedDuration(translate: LocaleContextProps['translate'], durationInSeconds: number): string { + const hours = Math.floor(durationInSeconds / 3600); + const minutes = Math.floor((durationInSeconds % 3600) / 60); + + return `${hours ? `${hours}${translate('common.hourAbbreviation')} ` : ''}${minutes}${translate('common.minuteAbbreviation')}`; } function doesDateBelongToAPastYear(date: string): boolean { @@ -928,6 +936,7 @@ const DateUtils = { getDifferenceInDaysFromNow, isValidDateString, getFormattedDurationBetweenDates, + getFormattedDuration, }; export default DateUtils; From 72b825e0e43d1bfad9389dc7ea98ac9ccb892ff3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 13:09:36 +0100 Subject: [PATCH 62/75] Refactor FlightTripDetails component to use reservation object --- src/pages/Travel/FlightTripDetails.tsx | 129 +++++++++++-------------- src/pages/Travel/TripDetailsPage.tsx | 1 + src/types/onyx/Transaction.ts | 3 + 3 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index d407dfb72aa4d..43e8e3e5f2279 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -1,135 +1,124 @@ import React from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; +import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import RenderHTML from '@components/RenderHTML'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; -import * as TripReservationUtils from '@libs/TripReservationUtils'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {PersonalDetails, Transaction} from '@src/types/onyx'; +import type {PersonalDetails} from '@src/types/onyx'; +import type {Reservation} from '@src/types/onyx/Transaction'; type FlightTripDetailsProps = { - transaction: OnyxEntry; + reservation: Reservation; + prevReservation: Reservation | undefined; personalDetails: OnyxEntry; - reservationIndex: number; }; -function FlightTripDetails({transaction, personalDetails, reservationIndex}: FlightTripDetailsProps) { +function FlightTripDetails({reservation, prevReservation, personalDetails}: FlightTripDetailsProps) { const styles = useThemeStyles(); const theme = useTheme(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const flightReservation = transaction?.receipt?.reservationList?.at(reservationIndex); + const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.start.date)); + const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.end.date)); - if (!flightReservation) { - return null; - } - - const reservationIcon = TripReservationUtils.getTripReservationIcon(flightReservation.type); - const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.start.date)); - const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(flightReservation.end.date)); - - const prevFlightStartDate = reservationIndex > 0 && transaction?.receipt?.reservationList?.at(reservationIndex - 1)?.end.date; - const layover = prevFlightStartDate && DateUtils.getFormattedDurationBetweenDates(new Date(prevFlightStartDate), new Date(flightReservation.start.date)); + const prevFlightEndDate = prevReservation?.end.date; + const layover = prevFlightEndDate && DateUtils.getFormattedDurationBetweenDates(translate, new Date(prevFlightEndDate), new Date(reservation.start.date)); + const flightDuration = DateUtils.getFormattedDuration(translate, reservation.duration); + const flightRouteDescription = `${reservation.start.cityName} (${reservation.start.shortName}) ${translate('common.conjunctionTo')} ${reservation.end.cityName} (${ + reservation.end.shortName + })`; return ( <> - + {flightRouteDescription} {!!layover && ( - <> - + - - + + )} - - + + {startDate.hour}} + helperText={`${reservation.start.longName} (${reservation.start.shortName})${reservation.arrivalGate?.terminal ? `, ${reservation.arrivalGate?.terminal}` : ''}`} helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> {endDate.hour}} + helperText={`${reservation.end.longName} (${reservation.end.shortName})`} helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> - {!!flightReservation.route?.number && ( + {!!reservation.route?.number && ( )} - {!!flightReservation.route?.class && ( + {!!reservation.route?.class && ( )} - {!!flightReservation.confirmations?.at(0)?.value && ( + {!!reservation.confirmations?.at(0)?.value && ( )} + + ); } diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index fd117ce8b7907..2907775109ccf 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -69,6 +69,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { {!!reservation && reservationType === CONST.RESERVATION_TYPE.FLIGHT && ( 0 ? transaction?.receipt?.reservationList?.at(route.params.reservationIndex - 1) : undefined} reservation={reservation} personalDetails={personalDetails} /> diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index e3937fee3c333..b5d7bbd8d348f 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -219,6 +219,9 @@ type Reservation = { /** In flight and car reservations, this represents the number of passengers */ numPassengers?: number; + /** In flight reservations, this represents the flight duration in seconds */ + duration: number; + /** In hotel reservations, this represents the number of rooms reserved */ numberOfRooms?: number; From 6466aec45a505d3b2a1a84a12a39a31d46bda151 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Dec 2024 13:24:26 +0100 Subject: [PATCH 63/75] Refactor TrainTripDetails component to use reservation object for rendering details --- src/pages/Travel/TrainTripDetails.tsx | 106 ++++++++++++-------------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index afaac7fdbf8ef..e1ba7aa9aacff 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -4,107 +4,101 @@ import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; -import * as TripReservationUtils from '@libs/TripReservationUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails, Transaction} from '@src/types/onyx'; +import type {PersonalDetails} from '@src/types/onyx'; +import type {Reservation} from '@src/types/onyx/Transaction'; type TrainTripDetailsProps = { - transaction: OnyxEntry; + reservation: Reservation; personalDetails: OnyxEntry; - reservationIndex: number; }; -function TrainTripDetails({transaction, personalDetails, reservationIndex}: TrainTripDetailsProps) { +function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) { const styles = useThemeStyles(); - const theme = useTheme(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); - const trainReservation = transaction?.receipt?.reservationList?.at(reservationIndex); - - if (!trainReservation) { - return null; - } - - const reservationIcon = TripReservationUtils.getTripReservationIcon(trainReservation.type); - const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(trainReservation.start.date)); - const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(trainReservation.end.date)); + const startDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.start.date)); + const endDate = DateUtils.getFormattedTransportDateAndHour(new Date(reservation.end.date)); + const trainRouteDescription = `${reservation.start.longName} (${reservation.start.shortName}) ${translate('common.conjunctionTo')} ${reservation.end.longName} (${ + reservation.end.shortName + })`; + const trainDuration = reservation.duration && DateUtils.getFormattedDuration(translate, reservation.duration); return ( <> - {trainRouteDescription} + + - - + {startDate.hour}} + helperText={`${reservation.start.longName} (${reservation.start.shortName})`} helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> {endDate.hour}} + helperText={`${reservation.end.longName} (${reservation.end.shortName})`} helperTextStyle={[styles.pb3, styles.mtn2]} interactive={false} /> - {!!trainReservation.coachNumber && ( + {!!reservation.coachNumber && ( )} - {!!trainReservation.seatNumber && ( + {!!reservation.seatNumber && ( + + )} + {!!reservation.confirmations?.at(0)?.value && ( + + )} - {!!trainReservation.confirmations?.at(0)?.value && ( - - )} + ); } From 6dc72b3ba23091bb6fddff6b336000c4f98863ec Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 5 Dec 2024 11:48:25 +0100 Subject: [PATCH 64/75] Refactor trip details components to remove personal details prop and use traveler information from reservation object --- src/pages/Travel/CarTripDetails.tsx | 11 ++++------- src/pages/Travel/FlightTripDetails.tsx | 11 ++++------- src/pages/Travel/HotelTripDetails.tsx | 11 ++++------- src/pages/Travel/TrainTripDetails.tsx | 11 ++++------- src/pages/Travel/TripDetailsPage.tsx | 24 +++--------------------- src/types/onyx/Transaction.ts | 12 ++++++++++++ 6 files changed, 31 insertions(+), 49 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 920fa73d61407..3a5d64c26eb40 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -8,15 +7,13 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type CarTripDetailsProps = { reservation: Reservation; - personalDetails: OnyxEntry; }; -function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { +function CarTripDetails({reservation}: CarTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -74,10 +71,10 @@ function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { )} diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 43e8e3e5f2279..3b2a8605d575b 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -1,6 +1,5 @@ import React from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; @@ -13,16 +12,14 @@ import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type FlightTripDetailsProps = { reservation: Reservation; prevReservation: Reservation | undefined; - personalDetails: OnyxEntry; }; -function FlightTripDetails({reservation, prevReservation, personalDetails}: FlightTripDetailsProps) { +function FlightTripDetails({reservation, prevReservation}: FlightTripDetailsProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); @@ -112,10 +109,10 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 2f65a4e4cd3b8..a83ba657daaec 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -8,15 +7,13 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type HotelTripDetailsProps = { reservation: Reservation; - personalDetails: OnyxEntry; }; -function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) { +function HotelTripDetails({reservation}: HotelTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -68,10 +65,10 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) )} diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index e1ba7aa9aacff..8a258b1a2ca24 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -1,6 +1,5 @@ import React from 'react'; import {View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -9,15 +8,13 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; -import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type TrainTripDetailsProps = { reservation: Reservation; - personalDetails: OnyxEntry; }; -function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) { +function TrainTripDetails({reservation}: TrainTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -92,10 +89,10 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 2907775109ccf..a905d8bb1939b 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -39,8 +39,6 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); - const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); @@ -71,27 +69,11 @@ function TripDetailsPage({route}: TripDetailsPageProps) { 0 ? transaction?.receipt?.reservationList?.at(route.params.reservationIndex - 1) : undefined} reservation={reservation} - personalDetails={personalDetails} - /> - )} - {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && ( - - )} - {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && ( - - )} - {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && ( - )} + {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && } + {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && } + {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && } Date: Thu, 5 Dec 2024 12:00:43 +0100 Subject: [PATCH 65/75] Revert "Refactor trip details components to remove personal details prop and use traveler information from reservation object" This reverts commit 6dc72b3ba23091bb6fddff6b336000c4f98863ec. --- src/pages/Travel/CarTripDetails.tsx | 11 +++++++---- src/pages/Travel/FlightTripDetails.tsx | 11 +++++++---- src/pages/Travel/HotelTripDetails.tsx | 11 +++++++---- src/pages/Travel/TrainTripDetails.tsx | 11 +++++++---- src/pages/Travel/TripDetailsPage.tsx | 24 +++++++++++++++++++++--- src/types/onyx/Transaction.ts | 12 ------------ 6 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 3a5d64c26eb40..920fa73d61407 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -7,13 +8,15 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; +import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type CarTripDetailsProps = { reservation: Reservation; + personalDetails: OnyxEntry; }; -function CarTripDetails({reservation}: CarTripDetailsProps) { +function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -71,10 +74,10 @@ function CarTripDetails({reservation}: CarTripDetailsProps) { )} diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 3b2a8605d575b..43e8e3e5f2279 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; @@ -12,14 +13,16 @@ import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type FlightTripDetailsProps = { reservation: Reservation; prevReservation: Reservation | undefined; + personalDetails: OnyxEntry; }; -function FlightTripDetails({reservation, prevReservation}: FlightTripDetailsProps) { +function FlightTripDetails({reservation, prevReservation, personalDetails}: FlightTripDetailsProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); @@ -109,10 +112,10 @@ function FlightTripDetails({reservation, prevReservation}: FlightTripDetailsProp diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index a83ba657daaec..2f65a4e4cd3b8 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -7,13 +8,15 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; +import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type HotelTripDetailsProps = { reservation: Reservation; + personalDetails: OnyxEntry; }; -function HotelTripDetails({reservation}: HotelTripDetailsProps) { +function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -65,10 +68,10 @@ function HotelTripDetails({reservation}: HotelTripDetailsProps) { )} diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index 8a258b1a2ca24..e1ba7aa9aacff 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -1,5 +1,6 @@ import React from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; @@ -8,13 +9,15 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; import CONST from '@src/CONST'; +import type {PersonalDetails} from '@src/types/onyx'; import type {Reservation} from '@src/types/onyx/Transaction'; type TrainTripDetailsProps = { reservation: Reservation; + personalDetails: OnyxEntry; }; -function TrainTripDetails({reservation}: TrainTripDetailsProps) { +function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -89,10 +92,10 @@ function TrainTripDetails({reservation}: TrainTripDetailsProps) { diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index a905d8bb1939b..2907775109ccf 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -39,6 +39,8 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); + const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); @@ -69,11 +71,27 @@ function TripDetailsPage({route}: TripDetailsPageProps) { 0 ? transaction?.receipt?.reservationList?.at(route.params.reservationIndex - 1) : undefined} reservation={reservation} + personalDetails={personalDetails} + /> + )} + {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && ( + + )} + {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && ( + + )} + {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && ( + )} - {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && } - {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && } - {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && } Date: Thu, 5 Dec 2024 12:08:31 +0100 Subject: [PATCH 66/75] Update trip details components to use traveler information from reservation object (but also use avatars from personal details) --- src/pages/Travel/CarTripDetails.tsx | 4 ++-- src/pages/Travel/FlightTripDetails.tsx | 4 ++-- src/pages/Travel/HotelTripDetails.tsx | 4 ++-- src/pages/Travel/TrainTripDetails.tsx | 4 ++-- src/pages/Travel/TripDetailsPage.tsx | 18 ++++++++++++------ src/types/onyx/Transaction.ts | 12 ++++++++++++ 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index 920fa73d61407..bc13f76a18557 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -74,10 +74,10 @@ function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { )} diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 43e8e3e5f2279..58268c94dd1d7 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -112,10 +112,10 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 2f65a4e4cd3b8..5ee7b228c14fe 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -68,10 +68,10 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) )} diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index e1ba7aa9aacff..8fa7ff1bed011 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -92,10 +92,10 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 2907775109ccf..4e12f79809aa2 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -1,6 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; import {NativeModules} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -20,11 +21,17 @@ import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import type {PersonalDetailsList} from '@src/types/onyx'; +import type {Reservation} from '@src/types/onyx/Transaction'; import CarTripDetails from './CarTripDetails'; import FlightTripDetails from './FlightTripDetails'; import HotelTripDetails from './HotelTripDetails'; import TrainTripDetails from './TrainTripDetails'; +function pickTravelerPersonalDetails(personalDetails: OnyxEntry, reservation: Reservation | undefined) { + return Object.values(personalDetails ?? {})?.find((personalDetail) => personalDetail?.login === reservation?.travelerPersonalInfo?.email); +} + type TripDetailsPageProps = StackScreenProps; function TripDetailsPage({route}: TripDetailsPageProps) { @@ -39,11 +46,10 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); - const accountID = Object.keys(report?.participants ?? {}).at(0) ?? '-1'; - const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (data) => data?.[accountID]}); const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); + const [travelerPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: (personalDetails) => pickTravelerPersonalDetails(personalDetails, reservation)}); return ( 0 ? transaction?.receipt?.reservationList?.at(route.params.reservationIndex - 1) : undefined} reservation={reservation} - personalDetails={personalDetails} + personalDetails={travelerPersonalDetails} /> )} {!!reservation && reservationType === CONST.RESERVATION_TYPE.HOTEL && ( )} {!!reservation && reservationType === CONST.RESERVATION_TYPE.CAR && ( )} {!!reservation && reservationType === CONST.RESERVATION_TYPE.TRAIN && ( )} Date: Thu, 5 Dec 2024 14:33:41 +0100 Subject: [PATCH 67/75] Show duration for train, fix truncated confirmatiion number --- src/pages/Travel/TrainTripDetails.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index 8fa7ff1bed011..a16ec20fd2a6e 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -26,7 +26,7 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) const trainRouteDescription = `${reservation.start.longName} (${reservation.start.shortName}) ${translate('common.conjunctionTo')} ${reservation.end.longName} (${ reservation.end.shortName })`; - const trainDuration = reservation.duration && DateUtils.getFormattedDuration(translate, reservation.duration); + const trainDuration = DateUtils.getFormattedDurationBetweenDates(translate, new Date(reservation.start.date), new Date(reservation.end.date)); return ( <> @@ -79,16 +79,14 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) />
)} - {!!reservation.confirmations?.at(0)?.value && ( - - - - )}
+ {!!reservation.confirmations?.at(0)?.value && ( + + )} Date: Thu, 5 Dec 2024 15:49:48 +0100 Subject: [PATCH 68/75] Display passenger/guest conditionally --- src/pages/Travel/CarTripDetails.tsx | 22 +++++++++++++--------- src/pages/Travel/FlightTripDetails.tsx | 23 +++++++++++++---------- src/pages/Travel/HotelTripDetails.tsx | 22 +++++++++++++--------- src/pages/Travel/TrainTripDetails.tsx | 22 +++++++++++++--------- 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/pages/Travel/CarTripDetails.tsx b/src/pages/Travel/CarTripDetails.tsx index bc13f76a18557..09ffd3d2cad16 100644 --- a/src/pages/Travel/CarTripDetails.tsx +++ b/src/pages/Travel/CarTripDetails.tsx @@ -26,6 +26,8 @@ function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { ? `${translate('travel.carDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(reservation.cancellationDeadline)).date}` : reservation.cancellationPolicy; + const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; + return ( <> {reservation.vendor} @@ -72,15 +74,17 @@ function CarTripDetails({reservation, personalDetails}: CarTripDetailsProps) { interactive={false} /> )} - + {!!displayName && ( + + )} ); } diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 58268c94dd1d7..901286ef33b8a 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -37,6 +37,8 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig reservation.end.shortName })`; + const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; + return ( <> {flightRouteDescription} @@ -109,16 +111,17 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig
)}
- - + {!!displayName && ( + + )} ); } diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index 5ee7b228c14fe..bb500fa673fa4 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -26,6 +26,8 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) ? `${translate('travel.hotelDetails.cancellationUntil')} ${DateUtils.getFormattedTransportDateAndHour(new Date(reservation.cancellationDeadline)).date}` : reservation.cancellationPolicy; + const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; + return ( <> {reservation.start.longName} @@ -66,15 +68,17 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) interactive={false} /> )} - + {!!displayName && ( + + )} ); } diff --git a/src/pages/Travel/TrainTripDetails.tsx b/src/pages/Travel/TrainTripDetails.tsx index a16ec20fd2a6e..c832459813210 100644 --- a/src/pages/Travel/TrainTripDetails.tsx +++ b/src/pages/Travel/TrainTripDetails.tsx @@ -28,6 +28,8 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) })`; const trainDuration = DateUtils.getFormattedDurationBetweenDates(translate, new Date(reservation.start.date), new Date(reservation.end.date)); + const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; + return ( <> {trainRouteDescription} @@ -88,15 +90,17 @@ function TrainTripDetails({reservation, personalDetails}: TrainTripDetailsProps) /> )} - + {!!displayName && ( + + )} ); } From e09dc23aaf9a1546aa01d39342b07e1d9f7a3236 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 09:32:32 +0100 Subject: [PATCH 69/75] Use label size for layover --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/pages/Travel/FlightTripDetails.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 95ac762d0d2b5..f37ed7a8ac296 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2445,7 +2445,7 @@ const translations = { flight: 'Flight', flightDetails: { passenger: 'Passenger', - layover: ({layover}: FlightLayoverParams) => `You have a ${layover} layover before this flight`, + layover: ({layover}: FlightLayoverParams) => `You have a ${layover} layover before this flight`, takeOff: 'Take-off', landing: 'Landing', seat: 'Seat', diff --git a/src/languages/es.ts b/src/languages/es.ts index b327e73c39501..76b23e9055149 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2469,7 +2469,7 @@ const translations = { flight: 'Vuelo', flightDetails: { passenger: 'Pasajero', - layover: ({layover}: FlightLayoverParams) => `Tienes una escala de ${layover} antes de este vuelo`, + layover: ({layover}: FlightLayoverParams) => `Tienes una escala de ${layover} antes de este vuelo`, takeOff: 'Despegue', landing: 'Aterrizaje', seat: 'Asiento', diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 901286ef33b8a..2882527879d9d 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -38,7 +38,7 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig })`; const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; - + console.log(`%%% reservation`, reservation); return ( <> {flightRouteDescription} From e91d3a824d9c603b31f8472e99d90863caacccac Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 09:32:44 +0100 Subject: [PATCH 70/75] Remove console log from FlightTripDetails component --- src/pages/Travel/FlightTripDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Travel/FlightTripDetails.tsx b/src/pages/Travel/FlightTripDetails.tsx index 2882527879d9d..901286ef33b8a 100644 --- a/src/pages/Travel/FlightTripDetails.tsx +++ b/src/pages/Travel/FlightTripDetails.tsx @@ -38,7 +38,7 @@ function FlightTripDetails({reservation, prevReservation, personalDetails}: Flig })`; const displayName = personalDetails?.displayName ?? reservation.travelerPersonalInfo?.name; - console.log(`%%% reservation`, reservation); + return ( <> {flightRouteDescription} From 6b313498d810ae76b4b3d42a82e56277f1c1ec23 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 09:44:55 +0100 Subject: [PATCH 71/75] Refactor navigation parameters to use tripRoomReportID consistently across trip-related components --- src/components/ReportActionItem/MoneyRequestView.tsx | 8 ++------ src/components/ReportActionItem/TripDetailsView.tsx | 12 ++++++------ src/pages/Travel/TripDetailsPage.tsx | 3 +-- src/pages/Travel/TripSummaryPage.tsx | 4 ++-- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 46ff97fcc6db8..b48880c3ae76e 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -702,13 +702,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals onPress={() => { const reservations = transaction?.receipt?.reservationList?.length ?? 0; if (reservations > 1) { - Navigation.navigate( - ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute()), - ); + Navigation.navigate(ROUTES.TRAVEL_TRIP_SUMMARY.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute())); } - Navigation.navigate( - ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.parentReportID ?? '-1', transaction?.transactionID ?? '-1', 0, Navigation.getReportRHPActiveRoute()), - ); + Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', 0, Navigation.getReportRHPActiveRoute())); }} /> )} diff --git a/src/components/ReportActionItem/TripDetailsView.tsx b/src/components/ReportActionItem/TripDetailsView.tsx index 4fcd1906afc96..a7fdef547bf90 100644 --- a/src/components/ReportActionItem/TripDetailsView.tsx +++ b/src/components/ReportActionItem/TripDetailsView.tsx @@ -22,7 +22,7 @@ import type {Reservation, ReservationTimeDetails} from '@src/types/onyx/Transact type TripDetailsViewProps = { /** The active tripRoomReportID, used for Onyx subscription */ - tripRoomReportID?: string; + tripRoomReportID: string; /** Whether we should display the horizontal rule below the component */ shouldShowHorizontalRule: boolean; @@ -31,11 +31,11 @@ type TripDetailsViewProps = { type ReservationViewProps = { reservation: Reservation; transactionID: string; - reportID: string; + tripRoomReportID: string; reservationIndex: number; }; -function ReservationView({reservation, transactionID, reportID, reservationIndex}: ReservationViewProps) { +function ReservationView({reservation, transactionID, tripRoomReportID, reservationIndex}: ReservationViewProps) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -137,7 +137,7 @@ function ReservationView({reservation, transactionID, reportID, reservationIndex iconWidth={20} iconStyles={[StyleUtils.getTripReservationIconContainer(false), styles.mr3]} secondaryIconFill={theme.icon} - onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(reportID, transactionID, reservationIndex, Navigation.getReportRHPActiveRoute()))} + onPress={() => Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(tripRoomReportID, transactionID, reservationIndex, Navigation.getReportRHPActiveRoute()))} /> ); } @@ -162,13 +162,13 @@ function TripDetailsView({tripRoomReportID, shouldShowHorizontalRule}: TripDetai
<> - {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { + {reservationsData.map(({reservation, transactionID, reservationIndex}) => { return ( diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 4e12f79809aa2..7b6d0cde85c29 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -43,9 +43,8 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); - const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(report?.parentReportID); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(route.params.reportID); const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); diff --git a/src/pages/Travel/TripSummaryPage.tsx b/src/pages/Travel/TripSummaryPage.tsx index caf5d471c84c0..8a0a4f9c38b72 100644 --- a/src/pages/Travel/TripSummaryPage.tsx +++ b/src/pages/Travel/TripSummaryPage.tsx @@ -41,13 +41,13 @@ function TripSummaryPage({route}: TripSummaryPageProps) { shouldShowBackButton /> - {reservationsData.map(({reservation, transactionID, reportID, reservationIndex}) => { + {reservationsData.map(({reservation, transactionID, reservationIndex}) => { return ( From 9c82c76e0ef228591383ab3aa1c917936d25ba73 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 11:05:56 +0100 Subject: [PATCH 72/75] Fix modify trip and trip support buttons for all cases --- src/pages/Travel/TripDetailsPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 7b6d0cde85c29..c6ff3c23990f2 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -43,8 +43,10 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); + const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID ?? '-1'}`); + const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`); - const tripID = ReportUtils.getTripIDFromTransactionParentReportID(route.params.reportID); + const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.reportID); const reservationType = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0)?.type; const reservation = transaction?.receipt?.reservationList?.at(route.params.reservationIndex ?? 0); const reservationIcon = TripReservationUtils.getTripReservationIcon(reservation?.type); From c69639a6ada393bab6714e1af4228880bcc5c043 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 11:15:48 +0100 Subject: [PATCH 73/75] Add network status handling to disable buttons when offline --- src/pages/Travel/TripDetailsPage.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index c6ff3c23990f2..0ba086bff3255 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -10,6 +10,7 @@ import MenuItem from '@components/MenuItem'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePermissions from '@hooks/usePermissions'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -40,6 +41,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const {canUseSpotnanaTravel} = usePermissions(); + const {isOffline} = useNetwork(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); @@ -109,6 +111,7 @@ function TripDetailsPage({route}: TripDetailsPageProps) { Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); }} wrapperStyle={styles.mt3} + disabled={isOffline} /> { Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); }} + disabled={isOffline} />
From 0b6030ff28924ce995bc70ee9099b614910284a9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Dec 2024 15:38:55 +0100 Subject: [PATCH 74/75] Add loading state management for trip modification and support actions --- src/pages/Travel/TripDetailsPage.tsx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/pages/Travel/TripDetailsPage.tsx b/src/pages/Travel/TripDetailsPage.tsx index 0ba086bff3255..39094d1821e74 100644 --- a/src/pages/Travel/TripDetailsPage.tsx +++ b/src/pages/Travel/TripDetailsPage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; +import React, {useState} from 'react'; import {NativeModules} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {useOnyx} from 'react-native-onyx'; @@ -43,6 +43,9 @@ function TripDetailsPage({route}: TripDetailsPageProps) { const {canUseSpotnanaTravel} = usePermissions(); const {isOffline} = useNetwork(); + const [isModifyTripLoading, setIsModifyTripLoading] = useState(false); + const [isTripSupportLoading, setIsTripSupportLoading] = useState(false); + const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`); const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transaction?.reportID ?? '-1'}`); @@ -108,10 +111,14 @@ function TripDetailsPage({route}: TripDetailsPageProps) { iconRight={Expensicons.NewWindow} shouldShowRightIcon onPress={() => { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + setIsModifyTripLoading(true); + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID))?.finally(() => { + setIsModifyTripLoading(false); + }); }} wrapperStyle={styles.mt3} - disabled={isOffline} + shouldShowLoadingSpinnerIcon={isModifyTripLoading} + disabled={isModifyTripLoading || isOffline} /> { - Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID)); + setIsTripSupportLoading(true); + Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID))?.finally(() => { + setIsTripSupportLoading(false); + }); }} - disabled={isOffline} + shouldShowLoadingSpinnerIcon={isTripSupportLoading} + disabled={isTripSupportLoading || isOffline} /> From 8324be88317eac24e8a594a37d7b979b682fa330 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Tue, 10 Dec 2024 13:19:33 +0100 Subject: [PATCH 75/75] fix PR comments --- src/components/ReportActionItem/TripRoomPreview.tsx | 4 ++-- src/pages/Travel/HotelTripDetails.tsx | 1 + src/pages/Travel/TripDetailsPage.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/ReportActionItem/TripRoomPreview.tsx b/src/components/ReportActionItem/TripRoomPreview.tsx index f8537e4beacf9..764979651ee75 100644 --- a/src/components/ReportActionItem/TripRoomPreview.tsx +++ b/src/components/ReportActionItem/TripRoomPreview.tsx @@ -77,14 +77,14 @@ function ReservationView({reservation}: ReservationViewProps) { titleComponent = ( - {startName} + {startName} - {endName} + {endName} ); } diff --git a/src/pages/Travel/HotelTripDetails.tsx b/src/pages/Travel/HotelTripDetails.tsx index bb500fa673fa4..747dc3ceca70e 100644 --- a/src/pages/Travel/HotelTripDetails.tsx +++ b/src/pages/Travel/HotelTripDetails.tsx @@ -35,6 +35,7 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps) description={translate('common.address')} title={reservation.start.address} interactive={false} + numberOfLinesTitle={2} />