From 16eddba5ffc87e53340a31f91b69771eced640bc Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Fri, 23 May 2025 19:04:47 +0200 Subject: [PATCH 01/19] Add new type of reportAction and configure update-messages --- src/CONST.ts | 1 + src/languages/en.ts | 42 +++++ src/languages/es.ts | 44 +++++ src/libs/ReportActionsUtils.ts | 6 +- src/libs/ReportUtils.ts | 162 +++++++++++++++++ .../home/report/PureReportActionItem.tsx | 7 + src/types/onyx/OriginalMessage.ts | 168 ++++++++++++++++++ 7 files changed, 429 insertions(+), 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 4d7049271e78..a479912db4b6 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -1315,6 +1315,7 @@ const CONST = { TASK_COMPLETED: 'TASKCOMPLETED', TASK_EDITED: 'TASKEDITED', TASK_REOPENED: 'TASKREOPENED', + TRAVEL_UPDATE: 'TRAVEL_TRIP_ROOM_UPDATE', TRIP_PREVIEW: 'TRIPPREVIEW', UNAPPROVED: 'UNAPPROVED', UNHOLD: 'UNHOLD', diff --git a/src/languages/en.ts b/src/languages/en.ts index 1a53eae84b31..697a6fb39eda 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2910,6 +2910,48 @@ const translations = { title: 'Get started with travel today!', message: `Please contact your Account manager or salesteam@expensify.com to get a demo of travel and have it enabled for your company.`, }, + updates: { + bookingTicketed: ({ + airlineCode, + origin, + destination, + startDate, + confirmationID, + }: { + airlineCode: string; + origin: string; + startDate: string; + destination: string; + confirmationID: string; + }) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been booked. Confirmation code: ${confirmationID}`, + ticketVoided: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been voided.`, + ticketRefunded: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been refunded or exchange.`, + flightCancelled: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate}} has been canceled by the airline`, + flightScheduleChangePending: ({airlineCode}: {airlineCode: string}) => `The airline has proposed a schedule change for flight ${airlineCode}; we are awaiting confirmation.`, + flightScheduleChangeClosed: ({airlineCode, startDate}: {airlineCode: string; startDate: string}) => + `Schedule change confirmed: flight ${airlineCode} now departs at ${startDate}.`, + flightChanged: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been changed.`, + flightCabinChanged: ({airlineCode, cabinClass}: {cabinClass: string; airlineCode: string}) => `Your cabin class has been updated to ${cabinClass} on flight ${airlineCode}.`, + flightSeatConfirmed: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} has been confirmed.`, + flightSeatChanged: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} has been changed.`, + flightSeatCancelled: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} was removed.`, + paymentDeclined: 'Payment for your air booking failed. Please try again.', + bookingCancelledByTraveller: ({type, reservationID}: {type: string; reservationID: string}) => `You cancelled your ${type} reservation ${reservationID}.`, + bookingCancelledByVendor: ({type, reservationID}: {type: string; reservationID: string}) => `The vendor cancelled your ${type} reservation ${reservationID}.`, + bookingRebooked: ({type, confirmationID}: {type: string; confirmationID: string}) => `Your ${type} reservation was re-booked. New confirmation #:${confirmationID}.`, + bookingUpdated: ({type}: {type: string}) => `Your ${type} booking was updated. Review the new details in the itinerary.`, + railTicketRefund: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Your rail ticket for ${origin} → ${destination} on ${startDate} has been refunded. A credit will be processed.`, + railTicketExchange: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Your rail ticket for ${origin} → ${destination} on ${startDate} has been exchanged.`, + railTicketUpdate: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Your rail ticket for ${origin} → ${destination} on ${startDate} has been updated.`, + defaultUpdate: ({type}: {type: string}) => `Your ${type} reservation was updated.`, + }, }, workspace: { common: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 452a9d70620f..7234bccb7682 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2933,6 +2933,50 @@ const translations = { title: '¡Empieza a viajar hoy mismo!', message: `Por favor, contacta a tu gestor de cuenta o a salesteam@expensify.com para solicitar una demostración de Travel y habilitarlo para tu empresa.`, }, + updates: { + bookingTicketed: ({ + airlineCode, + origin, + destination, + startDate, + confirmationID, + }: { + airlineCode: string; + origin: string; + startDate: string; + destination: string; + confirmationID: string; + }) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) para el ${startDate} ha sido reservado. Código de confirmación: ${confirmationID}`, + ticketVoided: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Tu billete para el vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido anulado.`, + ticketRefunded: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Tu billete para el vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido reembolsado o cambiado.`, + flightCancelled: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido cancelado por la aerolínea.`, + flightScheduleChangePending: ({airlineCode}: {airlineCode: string}) => + `La aerolínea ha propuesto un cambio de horario para el vuelo ${airlineCode}; estamos esperando la confirmación.`, + flightScheduleChangeClosed: ({airlineCode, startDate}: {airlineCode: string; startDate: string}) => + `Cambio de horario confirmado: el vuelo ${airlineCode} ahora sale a las ${startDate}.`, + flightChanged: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido modificado.`, + flightCabinChanged: ({airlineCode, cabinClass}: {cabinClass: string; airlineCode: string}) => + `Tu clase de cabina ha sido actualizada a ${cabinClass} en el vuelo ${airlineCode}.`, + flightSeatConfirmed: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido confirmada.`, + flightSeatChanged: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido modificada.`, + flightSeatCancelled: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} fue eliminada.`, + paymentDeclined: 'El pago de tu reserva aérea ha fallado. Por favor, inténtalo de nuevo.', + bookingCancelledByTraveller: ({type, reservationID}: {type: string; reservationID: string}) => `Cancelaste tu reserva de ${type} ${reservationID}.`, + bookingCancelledByVendor: ({type, reservationID}: {type: string; reservationID: string}) => `El proveedor canceló tu reserva de ${type} ${reservationID}.`, + bookingRebooked: ({type, confirmationID}: {type: string; confirmationID: string}) => `Tu reserva de ${type} fue reprogramada. Nuevo número de confirmación: ${confirmationID}.`, + bookingUpdated: ({type}: {type: string}) => `Tu reserva de ${type} fue actualizada. Revisa los nuevos detalles en el itinerario.`, + railTicketRefund: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido reembolsado. Se procesará un crédito.`, + railTicketExchange: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido cambiado.`, + railTicketUpdate: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido actualizado.`, + defaultUpdate: ({type}: {type: string}) => `Tu reserva de ${type} fue actualizada.`, + }, }, workspace: { common: { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 934709be68c7..50af19394670 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -284,6 +284,10 @@ function isIntegrationMessageAction(reportAction: OnyxInputOrEntry return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.INTEGRATIONS_MESSAGE; } +function isTravelUpdate(reportAction: OnyxInputOrEntry): reportAction is ReportAction { + return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE); +} + /** * We are in the process of deprecating reportAction.originalMessage and will be setting the db version of "message" to reportAction.message in the future see: https://github.com/Expensify/App/issues/39797 * In the interim, we must check to see if we have an object or array for the reportAction.message, if we have an array we will use the originalMessage as this means we have not yet migrated. @@ -861,7 +865,7 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry, key: return false; } - if (isTripPreview(reportAction)) { + if (isTripPreview(reportAction) || isTravelUpdate(reportAction)) { return true; } diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 11882161be61..78b67742ec72 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -17,6 +17,7 @@ import type { OriginalMessageIOU, OriginalMessageModifiedExpense, OriginalMessageMovedTransaction, + OriginalMessageTravelUpdate, OriginalMessageUnreportedTransaction, } from 'src/types/onyx/OriginalMessage'; import type {SetRequired, TupleToUnion, ValueOf} from 'type-fest'; @@ -5827,6 +5828,166 @@ function getPolicyChangeMessage(action: ReportAction) { }); return message; } +function getTravelUpdateMessage(action: ReportAction) { + const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; + + switch (details?.operation) { + case 'BOOKING_TICKETED': + return translateLocal('travel.updates.bookingTicketed', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + confirmationID: details?.confirmations?.at(0)?.value ?? '', + }); + + case 'TICKET_VOIDED': + return translateLocal('travel.updates.ticketVoided', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + + case 'TICKET_REFUNDED': + return translateLocal('travel.updates.ticketRefunded', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + + case 'FLIGHT_CANCELLED': + return translateLocal('travel.updates.flightCancelled', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + + case 'FLIGHT_SCHEDULE_CHANGE_PENDING': + return translateLocal('travel.updates.flightScheduleChangePending', { + airlineCode: details?.route?.airlineCode ?? '', + }); + + case 'FLIGHT_SCHEDULE_CHANGE_CLOSED': + return translateLocal('travel.updates.flightScheduleChangeClosed', { + airlineCode: details?.route?.airlineCode ?? '', + startDate: details.start.date ?? '', + }); + + case 'FLIGHT_CHANGED': + return translateLocal('travel.updates.flightChanged', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + + case 'FLIGHT_CABIN_CHANGED': + return translateLocal('travel.updates.flightCabinChanged', { + airlineCode: details?.route?.airlineCode ?? '', + cabinClass: details.route?.class ?? '', + }); + + case 'FLIGHT_SEAT_CONFIRMED': + return translateLocal('travel.updates.flightSeatConfirmed', { + airlineCode: details?.route?.airlineCode ?? '', + }); + + case 'FLIGHT_SEAT_CHANGED': + return translateLocal('travel.updates.flightSeatChanged', { + airlineCode: details?.route?.airlineCode ?? '', + }); + + case 'FLIGHT_SEAT_CANCELLED': + return translateLocal('travel.updates.flightSeatCancelled', { + airlineCode: details?.route?.airlineCode ?? '', + }); + + case 'PAYMENT_DECLINED': + return translateLocal('travel.updates.paymentDeclined'); + + case 'BOOKING_CANCELED_BY_TRAVELER': + return translateLocal('travel.updates.bookingCancelledByTraveller', { + type: details.type ?? '', + reservationID: details.reservationID ?? '', + }); + + case 'BOOKING_CANCELED_BY_VENDOR': + return translateLocal('travel.updates.bookingCancelledByVendor', { + type: details.type ?? '', + reservationID: details.reservationID ?? '', + }); + + case 'BOOKING_REBOOKED': + return translateLocal('travel.updates.bookingRebooked', { + type: details.type ?? '', + confirmationID: details?.confirmations?.at(0)?.value ?? '' ?? '', + }); + + case 'BOOKING_UPDATED': + return translateLocal('travel.updates.bookingUpdated', { + type: details.type ?? '', + }); + + case 'TRIP_UPDATED': + if (details.type === 'car' || details.type === 'hotel') { + return translateLocal('travel.updates.defaultUpdate', { + type: details.type, + }); + } + if (details.type === 'train') { + return translateLocal('travel.updates.railTicketUpdate', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: details.start.date, + }); + } + return translateLocal('travel.updates.flightChanged', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + case 'BOOKING_OTHER_UPDATE': + if (details.type === 'car' || details.type === 'hotel') { + return translateLocal('travel.updates.defaultUpdate', { + type: details.type, + }); + } + if (details.type === 'train') { + return translateLocal('travel.updates.railTicketUpdate', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: details.start.date, + }); + } + return translateLocal('travel.updates.flightChanged', { + airlineCode: details?.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: details.start.date ?? '', + }); + + case 'REFUND': + return translateLocal('travel.updates.railTicketRefund', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: details.start.date, + }); + + case 'EXCHANGE': + return translateLocal('travel.updates.railTicketExchange', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: details.start.date, + }); + + default: + return 'Unknown trip update'; + } +} /** * @param iouReportID - the report ID of the IOU report the action belongs to @@ -10877,6 +11038,7 @@ export { getReimbursementDeQueuedOrCanceledActionMessage, getReimbursementQueuedActionMessage, getReportActionActorAccountID, + getTravelUpdateMessage, getReportDescription, getReportFieldKey, getReportIDFromLink, diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index ceeeff671d72..1e8d4145b082 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -132,6 +132,7 @@ import { getRejectedReportMessage, getReportAutomaticallyApprovedMessage, getReportAutomaticallySubmittedMessage, + getTravelUpdateMessage, getUnreportedTransactionMessage, getUpgradeWorkspaceMessage, getWhisperDisplayNames, @@ -1017,6 +1018,12 @@ function PureReportActionItem({ ${getMovedTransactionMessage(action)}`} /> ); + } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) { + children = ( + + ${getTravelUpdateMessage(action)}`} /> + + ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION) { children = ( diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 223a5ef47453..77d83de18fd6 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -497,6 +497,172 @@ type OriginalMessageModifiedExpense = { newAttendees?: Attendee[]; }; +/** Model of a `travel update` report action */ +type OriginalMessageTravelUpdate = { + /** Information about the arrival gate on the airport */ + arrivalGate?: { + /** Specific gate for arrival */ + gate: string; + + /** Terminal where the gate is located */ + terminal: string; + }; + + /** Information about a booked car */ + carInfo?: { + /** Engine specification */ + engine: string; + + /** Car model name */ + name: string; + }; + + /** Deadline when car can be cancelled without charges */ + cancellationDeadline: string; + + /** Policy of the cancellation */ + cancellationPolicy: string; + + /** Information about the travel service provider eg. hotel or airlines */ + company?: { + /** Full name of the company */ + longName: string; + + /** Contact phone number */ + phone: string; + + /** Short name */ + shortName: string; + }; + + /** Array of confirmation details */ + confirmations?: [ + { + /** Name of the confirmation detail */ + name: string; + + /** The actual confirmation value */ + value: string; + }, + ]; + + /** Information about the departure gate. */ + departureGate?: { + /** Specific gate for departure. */ + gated: string; + + /** Terminal where the gate is located. */ + terminal: string; + }; + + /** Duration of the route */ + duration?: number; + + /** Information about the destination */ + end: { + /** Detailed address of the endpoint */ + address?: string; + + /** Name of the starting-point city (for transport) */ + cityName?: string; + + /** Date-time when the journey ends */ + date: string; + + /** Full name of the endpoint */ + longName?: string; + + /** Similar to address */ + location?: string; + + /** Short name of the destination */ + shortName?: string; + }; + + /** Type or category of purchased fare */ + fareType?: string; + + /** If it's a new dot version of the app */ + isNewDot: boolean; + + /** When was the originalMessage last modified */ + lastModified: string; + + /** + * Type of operation + * TODO ADD CONST TYPES BOOKING_REBOOKED + * */ + operation: string; + + /** Type of payment used */ + paymentType?: string; + + /** ID identifying the booking */ + reservationID?: string; + + /** Class/type of a booked room */ + roomClass?: string; + + /** Details about flight or train route */ + route?: { + /** Flight code, provided by airline */ + airlineCode?: string; + /** Class of service */ + class?: string; + + /** Name of a train route */ + name?: string; + + /** Number of flight */ + number?: string; + }; + + /** Coach number in train travel */ + coachNumber?: string; + + /** Seat number in plane or train */ + seatNumber?: string; + + /** Information about origin of the trip */ + start: { + /** Detailed address of the starting point */ + address?: string; + + /** When the journey starts */ + date: string; + + /** Full name of the starting point */ + longName?: string; + + /** Short name of the starting point */ + shortName?: string; + + /** Similar to address */ + location?: string; + + /** Timezone offset of the starting point (for transport) */ + timezoneOffset?: string; + + /** Name of the city of departure (for transport) */ + cityName?: string; + }; + + /** Information about the traveler */ + travelerPersonalInfo: { + /** Email address */ + email: string; + + /** Traveller's name */ + name: string; + }; + + /** Type of pnr (flight, train, car, or hotel) */ + type: string; + + /** Vendor of the service */ + vendor?: string; +}; + /** Model of the `deleted transaction` report action */ type OriginalMessageDeletedTransaction = { /** The merchant of the transaction */ @@ -812,6 +978,7 @@ type OriginalMessageMap = { [CONST.REPORT.ACTIONS.TYPE.TASK_EDITED]: never; [CONST.REPORT.ACTIONS.TYPE.TASK_REOPENED]: never; [CONST.REPORT.ACTIONS.TYPE.TAKE_CONTROL]: never; + [CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE]: OriginalMessageTravelUpdate; [CONST.REPORT.ACTIONS.TYPE.UNAPPROVED]: OriginalMessageUnapproved; [CONST.REPORT.ACTIONS.TYPE.UNHOLD]: never; [CONST.REPORT.ACTIONS.TYPE.UNSHARE]: never; @@ -857,4 +1024,5 @@ export type { OriginalMessageChangePolicy, OriginalMessageUnreportedTransaction, OriginalMessageMovedTransaction, + OriginalMessageTravelUpdate, }; From c21d19231b04ab0dbd54842d5aec1d16e7c175de Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 26 May 2025 10:47:11 +0200 Subject: [PATCH 02/19] Fix type --- src/libs/ReportActionsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 50af19394670..068f6bdb4d11 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -284,7 +284,7 @@ function isIntegrationMessageAction(reportAction: OnyxInputOrEntry return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.INTEGRATIONS_MESSAGE; } -function isTravelUpdate(reportAction: OnyxInputOrEntry): reportAction is ReportAction { +function isTravelUpdate(reportAction: OnyxInputOrEntry): reportAction is ReportAction { return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE); } From acf47f53cebd3dcab69d5e6bb6acd38c6b1c9991 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 26 May 2025 11:46:09 +0200 Subject: [PATCH 03/19] Rerun the checks From 07241b46341f3551942ae86d81922963ed5d1cac Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 26 May 2025 13:12:03 +0200 Subject: [PATCH 04/19] Adjust translations --- src/languages/en.ts | 59 +++++++++++++++--------------------- src/languages/es.ts | 61 +++++++++++++++---------------------- src/languages/params.ts | 29 ++++++++++++++++++ src/libs/ReportUtils.ts | 67 +++++++++++++++++++++-------------------- 4 files changed, 111 insertions(+), 105 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 6a9c9b84f255..b0fec9eefae7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -11,6 +11,7 @@ import type { AddEmployeeParams, AddressLineParams, AdminCanceledRequestParams, + AirlineParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, @@ -82,6 +83,7 @@ import type { FileLimitParams, FiltersAmountBetweenParams, FlightLayoverParams, + FlightParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, @@ -133,6 +135,7 @@ import type { PayerSettledParams, PaySomeoneParams, PolicyExpenseChatNameParams, + RailTicketParams, ReconciliationWorksParams, RemovedFromApprovalWorkflowParams, RemovedTheRequestParams, @@ -184,6 +187,7 @@ import type { ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, + TravelTypeParams, TrialStartedTitleParams, UnapprovedParams, UnapproveWithIntegrationWarningParams, @@ -2929,46 +2933,31 @@ const translations = { message: `Please contact your Account manager or salesteam@expensify.com to get a demo of travel and have it enabled for your company.`, }, updates: { - bookingTicketed: ({ - airlineCode, - origin, - destination, - startDate, - confirmationID, - }: { - airlineCode: string; - origin: string; - startDate: string; - destination: string; - confirmationID: string; - }) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been booked. Confirmation code: ${confirmationID}`, - ticketVoided: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + bookingTicketed: ({airlineCode, origin, destination, startDate, confirmationID = ''}: FlightParams) => + `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been booked. Confirmation code: ${confirmationID}`, + ticketVoided: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been voided.`, - ticketRefunded: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + ticketRefunded: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been refunded or exchange.`, - flightCancelled: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + flightCancelled: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate}} has been canceled by the airline`, - flightScheduleChangePending: ({airlineCode}: {airlineCode: string}) => `The airline has proposed a schedule change for flight ${airlineCode}; we are awaiting confirmation.`, - flightScheduleChangeClosed: ({airlineCode, startDate}: {airlineCode: string; startDate: string}) => - `Schedule change confirmed: flight ${airlineCode} now departs at ${startDate}.`, - flightChanged: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => - `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been changed.`, - flightCabinChanged: ({airlineCode, cabinClass}: {cabinClass: string; airlineCode: string}) => `Your cabin class has been updated to ${cabinClass} on flight ${airlineCode}.`, - flightSeatConfirmed: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} has been confirmed.`, - flightSeatChanged: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} has been changed.`, - flightSeatCancelled: ({airlineCode}: {airlineCode: string}) => `Your seat assignment on flight ${airlineCode} was removed.`, + flightScheduleChangePending: ({airlineCode}: AirlineParams) => `The airline has proposed a schedule change for flight ${airlineCode}; we are awaiting confirmation.`, + flightScheduleChangeClosed: ({airlineCode, startDate}: AirlineParams) => `Schedule change confirmed: flight ${airlineCode} now departs at ${startDate}.`, + flightChanged: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been changed.`, + flightCabinChanged: ({airlineCode, cabinClass}: AirlineParams) => `Your cabin class has been updated to ${cabinClass} on flight ${airlineCode}.`, + flightSeatConfirmed: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} has been confirmed.`, + flightSeatChanged: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} has been changed.`, + flightSeatCancelled: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} was removed.`, paymentDeclined: 'Payment for your air booking failed. Please try again.', - bookingCancelledByTraveller: ({type, reservationID}: {type: string; reservationID: string}) => `You cancelled your ${type} reservation ${reservationID}.`, - bookingCancelledByVendor: ({type, reservationID}: {type: string; reservationID: string}) => `The vendor cancelled your ${type} reservation ${reservationID}.`, - bookingRebooked: ({type, confirmationID}: {type: string; confirmationID: string}) => `Your ${type} reservation was re-booked. New confirmation #:${confirmationID}.`, - bookingUpdated: ({type}: {type: string}) => `Your ${type} booking was updated. Review the new details in the itinerary.`, - railTicketRefund: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + bookingCancelledByTraveller: ({type, id = ''}: TravelTypeParams) => `You cancelled your ${type} reservation ${id}.`, + bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `The vendor cancelled your ${type} reservation ${id}.`, + bookingRebooked: ({type, id = ''}: TravelTypeParams) => `Your ${type} reservation was re-booked. New confirmation #:${id}.`, + bookingUpdated: ({type}: TravelTypeParams) => `Your ${type} booking was updated. Review the new details in the itinerary.`, + railTicketRefund: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been refunded. A credit will be processed.`, - railTicketExchange: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => - `Your rail ticket for ${origin} → ${destination} on ${startDate} has been exchanged.`, - railTicketUpdate: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => - `Your rail ticket for ${origin} → ${destination} on ${startDate} has been updated.`, - defaultUpdate: ({type}: {type: string}) => `Your ${type} reservation was updated.`, + railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been exchanged.`, + railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been updated.`, + defaultUpdate: ({type}: TravelTypeParams) => `Your ${type} reservation was updated.`, }, }, workspace: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 16acf2e0e159..e317c3a357c0 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -10,6 +10,7 @@ import type { AddEmployeeParams, AddressLineParams, AdminCanceledRequestParams, + AirlineParams, AlreadySignedInParams, ApprovalWorkflowErrorParams, ApprovedAmountParams, @@ -81,6 +82,7 @@ import type { FileLimitParams, FiltersAmountBetweenParams, FlightLayoverParams, + FlightParams, FormattedMaxLengthParams, ForwardedAmountParams, GoBackMessageParams, @@ -132,6 +134,7 @@ import type { PayerSettledParams, PaySomeoneParams, PolicyExpenseChatNameParams, + RailTicketParams, ReconciliationWorksParams, RemovedFromApprovalWorkflowParams, RemovedTheRequestParams, @@ -183,6 +186,7 @@ import type { ThreadSentMoneyReportNameParams, ToValidateLoginParams, TransferParams, + TravelTypeParams, TrialStartedTitleParams, UnapprovedParams, UnapproveWithIntegrationWarningParams, @@ -2952,48 +2956,31 @@ const translations = { message: `Por favor, contacta a tu gestor de cuenta o a salesteam@expensify.com para solicitar una demostración de Travel y habilitarlo para tu empresa.`, }, updates: { - bookingTicketed: ({ - airlineCode, - origin, - destination, - startDate, - confirmationID, - }: { - airlineCode: string; - origin: string; - startDate: string; - destination: string; - confirmationID: string; - }) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) para el ${startDate} ha sido reservado. Código de confirmación: ${confirmationID}`, - ticketVoided: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + bookingTicketed: ({airlineCode, origin, destination, startDate, confirmationID = ''}: FlightParams) => + `Tu vuelo ${airlineCode} (${origin} → ${destination}) para el ${startDate} ha sido reservado. Código de confirmación: ${confirmationID}`, + ticketVoided: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu billete para el vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido anulado.`, - ticketRefunded: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + ticketRefunded: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu billete para el vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido reembolsado o cambiado.`, - flightCancelled: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => + flightCancelled: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido cancelado por la aerolínea.`, - flightScheduleChangePending: ({airlineCode}: {airlineCode: string}) => - `La aerolínea ha propuesto un cambio de horario para el vuelo ${airlineCode}; estamos esperando la confirmación.`, - flightScheduleChangeClosed: ({airlineCode, startDate}: {airlineCode: string; startDate: string}) => - `Cambio de horario confirmado: el vuelo ${airlineCode} ahora sale a las ${startDate}.`, - flightChanged: ({airlineCode, origin, destination, startDate}: {airlineCode: string; origin: string; startDate: string; destination: string}) => - `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido modificado.`, - flightCabinChanged: ({airlineCode, cabinClass}: {cabinClass: string; airlineCode: string}) => - `Tu clase de cabina ha sido actualizada a ${cabinClass} en el vuelo ${airlineCode}.`, - flightSeatConfirmed: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido confirmada.`, - flightSeatChanged: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido modificada.`, - flightSeatCancelled: ({airlineCode}: {airlineCode: string}) => `Tu asignación de asiento en el vuelo ${airlineCode} fue eliminada.`, + flightScheduleChangePending: ({airlineCode}: AirlineParams) => `La aerolínea ha propuesto un cambio de horario para el vuelo ${airlineCode}; estamos esperando la confirmación.`, + flightScheduleChangeClosed: ({airlineCode, startDate}: AirlineParams) => `Cambio de horario confirmado: el vuelo ${airlineCode} ahora sale a las ${startDate}.`, + flightChanged: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido modificado.`, + flightCabinChanged: ({airlineCode, cabinClass}: AirlineParams) => `Tu clase de cabina ha sido actualizada a ${cabinClass} en el vuelo ${airlineCode}.`, + flightSeatConfirmed: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido confirmada.`, + flightSeatChanged: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido modificada.`, + flightSeatCancelled: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} fue eliminada.`, paymentDeclined: 'El pago de tu reserva aérea ha fallado. Por favor, inténtalo de nuevo.', - bookingCancelledByTraveller: ({type, reservationID}: {type: string; reservationID: string}) => `Cancelaste tu reserva de ${type} ${reservationID}.`, - bookingCancelledByVendor: ({type, reservationID}: {type: string; reservationID: string}) => `El proveedor canceló tu reserva de ${type} ${reservationID}.`, - bookingRebooked: ({type, confirmationID}: {type: string; confirmationID: string}) => `Tu reserva de ${type} fue reprogramada. Nuevo número de confirmación: ${confirmationID}.`, - bookingUpdated: ({type}: {type: string}) => `Tu reserva de ${type} fue actualizada. Revisa los nuevos detalles en el itinerario.`, - railTicketRefund: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => + bookingCancelledByTraveller: ({type, id = ''}: TravelTypeParams) => `Cancelaste tu reserva de ${type} ${id}.`, + bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `El proveedor canceló tu reserva de ${type} ${id}.`, + bookingRebooked: ({type, id = ''}: TravelTypeParams) => `Tu reserva de ${type} fue reprogramada. Nuevo número de confirmación: ${id}.`, + bookingUpdated: ({type}: TravelTypeParams) => `Tu reserva de ${type} fue actualizada. Revisa los nuevos detalles en el itinerario.`, + railTicketRefund: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido reembolsado. Se procesará un crédito.`, - railTicketExchange: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => - `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido cambiado.`, - railTicketUpdate: ({origin, destination, startDate}: {origin: string; destination: string; startDate: string}) => - `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido actualizado.`, - defaultUpdate: ({type}: {type: string}) => `Tu reserva de ${type} fue actualizada.`, + railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido cambiado.`, + railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido actualizado.`, + defaultUpdate: ({type}: TravelTypeParams) => `Tu reserva de ${type} fue actualizada.`, }, }, workspace: { diff --git a/src/languages/params.ts b/src/languages/params.ts index 31bff0ef90fc..f36d0441421f 100644 --- a/src/languages/params.ts +++ b/src/languages/params.ts @@ -692,6 +692,31 @@ type EmployeeInviteMessageParams = { name: string; }; +type FlightParams = { + startDate: string; + airlineCode: string; + origin: string; + destination: string; + confirmationID?: string; +}; + +type AirlineParams = { + airlineCode: string; + startDate?: string; + cabinClass?: string; +}; + +type RailTicketParams = { + origin: string; + destination: string; + startDate: string; +}; + +type TravelTypeParams = { + type: string; + id?: string; +}; + export type { AuthenticationErrorParams, ImportMembersSuccessfulDescriptionParams, @@ -938,4 +963,8 @@ export type { ReviewParams, CurrencyInputDisabledTextParams, EmployeeInviteMessageParams, + FlightParams, + AirlineParams, + RailTicketParams, + TravelTypeParams, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f49970f2e655..7242bfd52dee 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5881,79 +5881,80 @@ function getPolicyChangeMessage(action: ReportAction) { } function getTravelUpdateMessage(action: ReportAction) { const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; + const formattedStartDate = format(new Date(details.start.date), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); switch (details?.operation) { case 'BOOKING_TICKETED': return translateLocal('travel.updates.bookingTicketed', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', - confirmationID: details?.confirmations?.at(0)?.value ?? '', + startDate: formattedStartDate, + confirmationID: details.confirmations?.at(0)?.value, }); case 'TICKET_VOIDED': return translateLocal('travel.updates.ticketVoided', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'TICKET_REFUNDED': return translateLocal('travel.updates.ticketRefunded', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'FLIGHT_CANCELLED': return translateLocal('travel.updates.flightCancelled', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'FLIGHT_SCHEDULE_CHANGE_PENDING': return translateLocal('travel.updates.flightScheduleChangePending', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', }); case 'FLIGHT_SCHEDULE_CHANGE_CLOSED': return translateLocal('travel.updates.flightScheduleChangeClosed', { - airlineCode: details?.route?.airlineCode ?? '', - startDate: details.start.date ?? '', + airlineCode: details.route?.airlineCode ?? '', + startDate: formattedStartDate, }); case 'FLIGHT_CHANGED': return translateLocal('travel.updates.flightChanged', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'FLIGHT_CABIN_CHANGED': return translateLocal('travel.updates.flightCabinChanged', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', cabinClass: details.route?.class ?? '', }); case 'FLIGHT_SEAT_CONFIRMED': return translateLocal('travel.updates.flightSeatConfirmed', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', }); case 'FLIGHT_SEAT_CHANGED': return translateLocal('travel.updates.flightSeatChanged', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', }); case 'FLIGHT_SEAT_CANCELLED': return translateLocal('travel.updates.flightSeatCancelled', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', }); case 'PAYMENT_DECLINED': @@ -5961,25 +5962,25 @@ function getTravelUpdateMessage(action: ReportAction) { case 'BOOKING_CANCELED_BY_TRAVELER': return translateLocal('travel.updates.bookingCancelledByTraveller', { - type: details.type ?? '', - reservationID: details.reservationID ?? '', + type: details.type, + id: details.reservationID, }); case 'BOOKING_CANCELED_BY_VENDOR': return translateLocal('travel.updates.bookingCancelledByVendor', { - type: details.type ?? '', - reservationID: details.reservationID ?? '', + type: details.type, + id: details.reservationID, }); case 'BOOKING_REBOOKED': return translateLocal('travel.updates.bookingRebooked', { - type: details.type ?? '', - confirmationID: details?.confirmations?.at(0)?.value ?? '' ?? '', + type: details.type, + id: details.confirmations?.at(0)?.value, }); case 'BOOKING_UPDATED': return translateLocal('travel.updates.bookingUpdated', { - type: details.type ?? '', + type: details.type, }); case 'TRIP_UPDATED': @@ -5992,14 +5993,14 @@ function getTravelUpdateMessage(action: ReportAction) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: details.start.date, + startDate: formattedStartDate, }); } return translateLocal('travel.updates.flightChanged', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'BOOKING_OTHER_UPDATE': if (details.type === 'car' || details.type === 'hotel') { @@ -6011,28 +6012,28 @@ function getTravelUpdateMessage(action: ReportAction) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: details.start.date, + startDate: formattedStartDate, }); } return translateLocal('travel.updates.flightChanged', { - airlineCode: details?.route?.airlineCode ?? '', + airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', - startDate: details.start.date ?? '', + startDate: formattedStartDate, }); case 'REFUND': return translateLocal('travel.updates.railTicketRefund', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: details.start.date, + startDate: formattedStartDate, }); case 'EXCHANGE': return translateLocal('travel.updates.railTicketExchange', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: details.start.date, + startDate: formattedStartDate, }); default: From 759eb2cd081e424b6183f38e67fec3147aa133f0 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 26 May 2025 15:40:44 +0200 Subject: [PATCH 05/19] Adjust to review comments --- src/CONST.ts | 28 ++++++++++++++++++ src/libs/ReportUtils.ts | 48 +++++++++++++++---------------- src/types/onyx/OriginalMessage.ts | 6 ++-- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 2c0fd12d6c5e..3a5279296646 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -7112,6 +7112,34 @@ const CONST = { PROVISIONING: { ERROR_PERMISSION_DENIED: 'permissionDenied', }, + UPDATE_OPERATION_TYPE: { + BOOKING_TICKETED: 'BOOKING_TICKETED', + TICKET_VOIDED: 'TICKET_VOIDED', + TICKET_REFUNDED: 'TICKET_REFUNDED', + FLIGHT_CANCELLED: 'FLIGHT_CANCELLED', + FLIGHT_SCHEDULE_CHANGE_PENDING: 'FLIGHT_SCHEDULE_CHANGE_PENDING', + FLIGHT_SCHEDULE_CHANGE_CLOSED: 'FLIGHT_SCHEDULE_CHANGE_CLOSED', + FLIGHT_CHANGED: 'FLIGHT_CHANGED', + FLIGHT_CABIN_CHANGED: 'FLIGHT_CABIN_CHANGED', + FLIGHT_SEAT_CONFIRMED: 'FLIGHT_SEAT_CONFIRMED', + FLIGHT_SEAT_CHANGED: 'FLIGHT_SEAT_CHANGED', + FLIGHT_SEAT_CANCELLED: 'FLIGHT_SEAT_CANCELLED', + PAYMENT_DECLINED: 'PAYMENT_DECLINED', + BOOKING_CANCELED_BY_TRAVELER: 'BOOKING_CANCELED_BY_TRAVELER', + BOOKING_CANCELED_BY_VENDOR: 'BOOKING_CANCELED_BY_VENDOR', + BOOKING_REBOOKED: 'BOOKING_REBOOKED', + BOOKING_UPDATED: 'BOOKING_UPDATED', + TRIP_UPDATED: 'TRIP_UPDATED', + BOOKING_OTHER_UPDATE: 'BOOKING_OTHER_UPDATE', + REFUND: 'REFUND', + EXCHANGE: 'EXCHANGE', + }, + RESERVATION_TYPE: { + TRAIN: 'train', + FLIGHT: 'flight', + CAR: 'car', + HOTEL: 'hotel', + }, }, LAST_PAYMENT_METHOD: { LAST_USED: 'lastUsed', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7242bfd52dee..5d9fd9347b5f 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5884,7 +5884,7 @@ function getTravelUpdateMessage(action: ReportAction) { const formattedStartDate = format(new Date(details.start.date), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); switch (details?.operation) { - case 'BOOKING_TICKETED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: return translateLocal('travel.updates.bookingTicketed', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', @@ -5893,7 +5893,7 @@ function getTravelUpdateMessage(action: ReportAction) { confirmationID: details.confirmations?.at(0)?.value, }); - case 'TICKET_VOIDED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_VOIDED: return translateLocal('travel.updates.ticketVoided', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', @@ -5901,7 +5901,7 @@ function getTravelUpdateMessage(action: ReportAction) { startDate: formattedStartDate, }); - case 'TICKET_REFUNDED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_REFUNDED: return translateLocal('travel.updates.ticketRefunded', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', @@ -5909,7 +5909,7 @@ function getTravelUpdateMessage(action: ReportAction) { startDate: formattedStartDate, }); - case 'FLIGHT_CANCELLED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CANCELLED: return translateLocal('travel.updates.flightCancelled', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', @@ -5917,18 +5917,18 @@ function getTravelUpdateMessage(action: ReportAction) { startDate: formattedStartDate, }); - case 'FLIGHT_SCHEDULE_CHANGE_PENDING': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_PENDING: return translateLocal('travel.updates.flightScheduleChangePending', { airlineCode: details.route?.airlineCode ?? '', }); - case 'FLIGHT_SCHEDULE_CHANGE_CLOSED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_CLOSED: return translateLocal('travel.updates.flightScheduleChangeClosed', { airlineCode: details.route?.airlineCode ?? '', startDate: formattedStartDate, }); - case 'FLIGHT_CHANGED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CHANGED: return translateLocal('travel.updates.flightChanged', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', @@ -5936,60 +5936,60 @@ function getTravelUpdateMessage(action: ReportAction) { startDate: formattedStartDate, }); - case 'FLIGHT_CABIN_CHANGED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CABIN_CHANGED: return translateLocal('travel.updates.flightCabinChanged', { airlineCode: details.route?.airlineCode ?? '', cabinClass: details.route?.class ?? '', }); - case 'FLIGHT_SEAT_CONFIRMED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CONFIRMED: return translateLocal('travel.updates.flightSeatConfirmed', { airlineCode: details.route?.airlineCode ?? '', }); - case 'FLIGHT_SEAT_CHANGED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CHANGED: return translateLocal('travel.updates.flightSeatChanged', { airlineCode: details.route?.airlineCode ?? '', }); - case 'FLIGHT_SEAT_CANCELLED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CANCELLED: return translateLocal('travel.updates.flightSeatCancelled', { airlineCode: details.route?.airlineCode ?? '', }); - case 'PAYMENT_DECLINED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.PAYMENT_DECLINED: return translateLocal('travel.updates.paymentDeclined'); - case 'BOOKING_CANCELED_BY_TRAVELER': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_TRAVELER: return translateLocal('travel.updates.bookingCancelledByTraveller', { type: details.type, id: details.reservationID, }); - case 'BOOKING_CANCELED_BY_VENDOR': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_VENDOR: return translateLocal('travel.updates.bookingCancelledByVendor', { type: details.type, id: details.reservationID, }); - case 'BOOKING_REBOOKED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_REBOOKED: return translateLocal('travel.updates.bookingRebooked', { type: details.type, id: details.confirmations?.at(0)?.value, }); - case 'BOOKING_UPDATED': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_UPDATED: return translateLocal('travel.updates.bookingUpdated', { type: details.type, }); - case 'TRIP_UPDATED': - if (details.type === 'car' || details.type === 'hotel') { + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TRIP_UPDATED: + if (details.type === CONST.TRAVEL.RESERVATION_TYPE.CAR || details.type === CONST.TRAVEL.RESERVATION_TYPE.HOTEL) { return translateLocal('travel.updates.defaultUpdate', { type: details.type, }); } - if (details.type === 'train') { + if (details.type === CONST.TRAVEL.RESERVATION_TYPE.TRAIN) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', @@ -6002,13 +6002,13 @@ function getTravelUpdateMessage(action: ReportAction) { destination: details.end?.shortName ?? '', startDate: formattedStartDate, }); - case 'BOOKING_OTHER_UPDATE': - if (details.type === 'car' || details.type === 'hotel') { + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_OTHER_UPDATE: + if (details.type === CONST.TRAVEL.RESERVATION_TYPE.CAR || details.type === CONST.TRAVEL.RESERVATION_TYPE.HOTEL) { return translateLocal('travel.updates.defaultUpdate', { type: details.type, }); } - if (details.type === 'train') { + if (details.type === CONST.TRAVEL.RESERVATION_TYPE.TRAIN) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', @@ -6022,14 +6022,14 @@ function getTravelUpdateMessage(action: ReportAction) { startDate: formattedStartDate, }); - case 'REFUND': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.REFUND: return translateLocal('travel.updates.railTicketRefund', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', startDate: formattedStartDate, }); - case 'EXCHANGE': + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.EXCHANGE: return translateLocal('travel.updates.railTicketExchange', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 77d83de18fd6..0e4337513261 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -589,10 +589,8 @@ type OriginalMessageTravelUpdate = { lastModified: string; /** - * Type of operation - * TODO ADD CONST TYPES BOOKING_REBOOKED - * */ - operation: string; + * Type of operation */ + operation: ValueOf; /** Type of payment used */ paymentType?: string; From 367fb5e80e77efbd8a3d4e52e4df51a19be06ab0 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Wed, 28 May 2025 11:42:03 +0200 Subject: [PATCH 06/19] Add date formatting --- src/libs/ReportUtils.ts | 5 +++-- src/pages/home/report/PureReportActionItem.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5d9fd9347b5f..acf26760a6c9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5879,9 +5879,10 @@ function getPolicyChangeMessage(action: ReportAction) { }); return message; } -function getTravelUpdateMessage(action: ReportAction) { +function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined)=> string) { const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; - const formattedStartDate = format(new Date(details.start.date), CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); + const formattedStartDate = formatDate(details.start.date, false) + switch (details?.operation) { case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 2221c8a3a814..044e87323e80 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -405,7 +405,7 @@ function PureReportActionItem({ policies, shouldShowBorder, }: PureReportActionItemProps) { - const {translate} = useLocalize(); + const {translate, datetimeToCalendarTime} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); const reportID = report?.reportID ?? action?.reportID; const theme = useTheme(); @@ -1015,7 +1015,7 @@ function PureReportActionItem({ } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) { children = ( - ${getTravelUpdateMessage(action)}`} /> + ${getTravelUpdateMessage(action, datetimeToCalendarTime)}`} /> ); } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION) { From 0b845d952189790bf7d523a00455995ddcc2e564 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Wed, 28 May 2025 11:55:27 +0200 Subject: [PATCH 07/19] Prettier fix --- src/libs/ReportUtils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 33dafe71044d..0e6f0053e3aa 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5901,10 +5901,9 @@ function getPolicyChangeMessage(action: ReportAction) { }); return message; } -function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined)=> string) { +function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; - const formattedStartDate = formatDate(details.start.date, false) - + const formattedStartDate = formatDate(details.start.date, false); switch (details?.operation) { case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: From b5249fdf32de7cc14c1b362cfbed9e4c5b1e3e09 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 2 Jun 2025 12:13:18 +0200 Subject: [PATCH 08/19] Adjus types --- src/CONST.ts | 6 -- src/libs/ReportUtils.ts | 8 +- src/types/onyx/OriginalMessage.ts | 164 +----------------------------- src/types/onyx/Transaction.ts | 22 +++- 4 files changed, 26 insertions(+), 174 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 981792772433..e98c08c59fdb 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -7133,12 +7133,6 @@ const CONST = { REFUND: 'REFUND', EXCHANGE: 'EXCHANGE', }, - RESERVATION_TYPE: { - TRAIN: 'train', - FLIGHT: 'flight', - CAR: 'car', - HOTEL: 'hotel', - }, }, LAST_PAYMENT_METHOD: { LAST_USED: 'lastUsed', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 0e6f0053e3aa..e5862ab89e65 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6006,12 +6006,12 @@ function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: str }); case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TRIP_UPDATED: - if (details.type === CONST.TRAVEL.RESERVATION_TYPE.CAR || details.type === CONST.TRAVEL.RESERVATION_TYPE.HOTEL) { + if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { return translateLocal('travel.updates.defaultUpdate', { type: details.type, }); } - if (details.type === CONST.TRAVEL.RESERVATION_TYPE.TRAIN) { + if (details.type === CONST.RESERVATION_TYPE.TRAIN) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', @@ -6025,12 +6025,12 @@ function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: str startDate: formattedStartDate, }); case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_OTHER_UPDATE: - if (details.type === CONST.TRAVEL.RESERVATION_TYPE.CAR || details.type === CONST.TRAVEL.RESERVATION_TYPE.HOTEL) { + if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { return translateLocal('travel.updates.defaultUpdate', { type: details.type, }); } - if (details.type === CONST.TRAVEL.RESERVATION_TYPE.TRAIN) { + if (details.type === CONST.RESERVATION_TYPE.TRAIN) { return translateLocal('travel.updates.railTicketUpdate', { origin: details.start.cityName ?? details.start.shortName ?? '', destination: details.end.cityName ?? details.end.shortName ?? '', diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 2038da6e99fd..bc61b8e7eceb 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -5,6 +5,7 @@ import type {Attendee} from './IOU'; import type {OldDotOriginalMessageMap} from './OldDotAction'; import type {AllConnectionName} from './Policy'; import type ReportActionName from './ReportActionName'; +import type { Reservation } from './Transaction'; /** Types of join workspace resolutions */ type JoinWorkspaceResolution = ValueOf; @@ -498,167 +499,12 @@ type OriginalMessageModifiedExpense = { }; /** Model of a `travel update` report action */ -type OriginalMessageTravelUpdate = { - /** Information about the arrival gate on the airport */ - arrivalGate?: { - /** Specific gate for arrival */ - gate: string; - - /** Terminal where the gate is located */ - terminal: string; - }; - - /** Information about a booked car */ - carInfo?: { - /** Engine specification */ - engine: string; - - /** Car model name */ - name: string; - }; - - /** Deadline when car can be cancelled without charges */ - cancellationDeadline: string; - - /** Policy of the cancellation */ - cancellationPolicy: string; - - /** Information about the travel service provider eg. hotel or airlines */ - company?: { - /** Full name of the company */ - longName: string; - - /** Contact phone number */ - phone: string; - - /** Short name */ - shortName: string; - }; - - /** Array of confirmation details */ - confirmations?: [ - { - /** Name of the confirmation detail */ - name: string; - - /** The actual confirmation value */ - value: string; - }, - ]; - - /** Information about the departure gate. */ - departureGate?: { - /** Specific gate for departure. */ - gated: string; - - /** Terminal where the gate is located. */ - terminal: string; - }; - - /** Duration of the route */ - duration?: number; - - /** Information about the destination */ - end: { - /** Detailed address of the endpoint */ - address?: string; - - /** Name of the starting-point city (for transport) */ - cityName?: string; - - /** Date-time when the journey ends */ - date: string; - - /** Full name of the endpoint */ - longName?: string; - - /** Similar to address */ - location?: string; - - /** Short name of the destination */ - shortName?: string; - }; - - /** Type or category of purchased fare */ - fareType?: string; - - /** If it's a new dot version of the app */ - isNewDot: boolean; +type OriginalMessageTravelUpdate = Reservation & UpdateOperationType; - /** When was the originalMessage last modified */ - lastModified: string; - - /** - * Type of operation */ +/** Travel update operation type */ +type UpdateOperationType = { + /** Type of operation */ operation: ValueOf; - - /** Type of payment used */ - paymentType?: string; - - /** ID identifying the booking */ - reservationID?: string; - - /** Class/type of a booked room */ - roomClass?: string; - - /** Details about flight or train route */ - route?: { - /** Flight code, provided by airline */ - airlineCode?: string; - /** Class of service */ - class?: string; - - /** Name of a train route */ - name?: string; - - /** Number of flight */ - number?: string; - }; - - /** Coach number in train travel */ - coachNumber?: string; - - /** Seat number in plane or train */ - seatNumber?: string; - - /** Information about origin of the trip */ - start: { - /** Detailed address of the starting point */ - address?: string; - - /** When the journey starts */ - date: string; - - /** Full name of the starting point */ - longName?: string; - - /** Short name of the starting point */ - shortName?: string; - - /** Similar to address */ - location?: string; - - /** Timezone offset of the starting point (for transport) */ - timezoneOffset?: string; - - /** Name of the city of departure (for transport) */ - cityName?: string; - }; - - /** Information about the traveler */ - travelerPersonalInfo: { - /** Email address */ - email: string; - - /** Traveller's name */ - name: string; - }; - - /** Type of pnr (flight, train, car, or hotel) */ - type: string; - - /** Vendor of the service */ - vendor?: string; }; /** Model of the `deleted transaction` report action */ diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 75983b0ef1fa..a0ad7a9a6f8f 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -256,7 +256,7 @@ type Reservation = { numPassengers?: number; /** In flight reservations, this represents the flight duration in seconds */ - duration: number; + duration?: number; /** In hotel reservations, this represents the number of rooms reserved */ numberOfRooms?: number; @@ -288,11 +288,11 @@ type Reservation = { /** Payment type of the reservation */ paymentType?: string; + /** Departure gate details */ + departureGate?: Gate; + /** Arrival gate details */ - arrivalGate?: { - /** Arrival terminal number */ - terminal: string; - }; + arrivalGate?: Gate; /** Coach number for rail */ coachNumber?: string; @@ -302,8 +302,20 @@ type Reservation = { /** This represents the details of the traveler */ travelerPersonalInfo?: TravelerPersonalDetails; + + /** Type or category of purchased fare */ + fareType?: string; }; +/** Model of gate for flight reservation */ +type Gate = { + /** Terminal number */ + terminal: string; + + /** Specific gate number */ + gate: string; +} + /** Model of trip reservation time details */ type ReservationTimeDetails = { /** Date timestamp */ From 54eb2d430c43f23ffc00d0d0d4cf2c1dba3a77f7 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 2 Jun 2025 12:43:40 +0200 Subject: [PATCH 09/19] Prettier fix --- Mobile-Expensify | 2 +- src/types/onyx/OriginalMessage.ts | 4 ++-- src/types/onyx/Transaction.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 3821cc5ea7d8..280dded8a91b 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 3821cc5ea7d8babee4cd44259f58f1380ece9fa1 +Subproject commit 280dded8a91b91cc051826755367d404958f8191 diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index bc61b8e7eceb..151fcfe9e988 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -5,7 +5,7 @@ import type {Attendee} from './IOU'; import type {OldDotOriginalMessageMap} from './OldDotAction'; import type {AllConnectionName} from './Policy'; import type ReportActionName from './ReportActionName'; -import type { Reservation } from './Transaction'; +import type {Reservation} from './Transaction'; /** Types of join workspace resolutions */ type JoinWorkspaceResolution = ValueOf; @@ -503,7 +503,7 @@ type OriginalMessageTravelUpdate = Reservation & UpdateOperationType; /** Travel update operation type */ type UpdateOperationType = { - /** Type of operation */ + /** Type of operation */ operation: ValueOf; }; diff --git a/src/types/onyx/Transaction.ts b/src/types/onyx/Transaction.ts index 062526e5a5b6..6898997336c0 100644 --- a/src/types/onyx/Transaction.ts +++ b/src/types/onyx/Transaction.ts @@ -317,7 +317,7 @@ type Gate = { /** Specific gate number */ gate: string; -} +}; /** Model of trip reservation time details */ type ReservationTimeDetails = { From aaec8b0e7912289bf2877ca96a247f444dce069d Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Mon, 2 Jun 2025 12:45:07 +0200 Subject: [PATCH 10/19] Update Mobile-Expensify version --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 280dded8a91b..3821cc5ea7d8 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 280dded8a91b91cc051826755367d404958f8191 +Subproject commit 3821cc5ea7d8babee4cd44259f58f1380ece9fa1 From fd1913b29e0a85694c9a8c50eb1fee6b94ccabe5 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 22:54:33 +0200 Subject: [PATCH 11/19] Add new report action type handling --- src/libs/OptionsListUtils.ts | 15 +- src/libs/ReportActionsUtils.ts | 171 +++++++++++++++++- src/libs/ReportUtils.ts | 168 +---------------- src/libs/SidebarUtils.ts | 3 + .../report/ContextMenu/ContextMenuActions.tsx | 3 + .../home/report/PureReportActionItem.tsx | 2 +- 6 files changed, 191 insertions(+), 171 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index a22a35a01c72..6405a57e20c6 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -65,6 +65,7 @@ import { getReportActionMessageText, getRetractedMessage, getSortedReportActions, + getTravelUpdateMessage, getUpdateRoomDescriptionMessage, isActionableAddPaymentCard, isActionableJoinRequest, @@ -642,7 +643,7 @@ function getAlternateText(option: OptionData, {showChatPreviewLine = false, forc return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix - : formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? (option.participantsList.at(0)?.login ?? '') : ''); + : formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); } /** @@ -784,7 +785,7 @@ function getLastMessageTextForReport( isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(lastReportAction) ) { - const wasSubmittedViaHarvesting = !isMarkAsClosedAction(lastReportAction) ? (getOriginalMessage(lastReportAction)?.harvesting ?? false) : false; + const wasSubmittedViaHarvesting = !isMarkAsClosedAction(lastReportAction) ? getOriginalMessage(lastReportAction)?.harvesting ?? false : false; if (wasSubmittedViaHarvesting) { lastMessageTextFromReport = translateLocal('iou.automaticallySubmitted'); } else { @@ -831,6 +832,8 @@ function getLastMessageTextForReport( lastMessageTextFromReport = getReopenedMessage(); } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.CHANGE_POLICY)) { lastMessageTextFromReport = getPolicyChangeMessage(lastReportAction); + } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { + lastMessageTextFromReport = getTravelUpdateMessage(lastReportAction); } // we do not want to show report closed in LHN for non archived report so use getReportLastMessage as fallback instead of lastMessageText from report @@ -921,7 +924,7 @@ function createOption(accountIDs: number[], personalDetails: OnyxInputOrEntry iouAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); if (lastIOUAction) { lastIOUCreationDate = lastIOUAction.lastModified; diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 82eedba16647..464591c8808b 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1,3 +1,4 @@ +import {format} from 'date-fns'; import {fastMerge, Str} from 'expensify-common'; import clone from 'lodash/clone'; import lodashFindLast from 'lodash/findLast'; @@ -11,7 +12,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Card, Locale, OnyxInputOrEntry, PrivatePersonalDetails} from '@src/types/onyx'; -import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration} from '@src/types/onyx/OriginalMessage'; +import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration, OriginalMessageTravelUpdate} from '@src/types/onyx/OriginalMessage'; import type {PolicyReportFieldType} from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; @@ -1508,6 +1509,168 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD } } +function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { + const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; + const formattedStartDate = formatDate?.(details.start.date, false) ?? format(details.start.date, CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); + + switch (details?.operation) { + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: + return translateLocal('travel.updates.bookingTicketed', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + confirmationID: details.confirmations?.at(0)?.value, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_VOIDED: + return translateLocal('travel.updates.ticketVoided', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_REFUNDED: + return translateLocal('travel.updates.ticketRefunded', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CANCELLED: + return translateLocal('travel.updates.flightCancelled', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_PENDING: + return translateLocal('travel.updates.flightScheduleChangePending', { + airlineCode: details.route?.airlineCode ?? '', + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_CLOSED: + return translateLocal('travel.updates.flightScheduleChangeClosed', { + airlineCode: details.route?.airlineCode ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CHANGED: + return translateLocal('travel.updates.flightChanged', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CABIN_CHANGED: + return translateLocal('travel.updates.flightCabinChanged', { + airlineCode: details.route?.airlineCode ?? '', + cabinClass: details.route?.class ?? '', + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CONFIRMED: + return translateLocal('travel.updates.flightSeatConfirmed', { + airlineCode: details.route?.airlineCode ?? '', + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CHANGED: + return translateLocal('travel.updates.flightSeatChanged', { + airlineCode: details.route?.airlineCode ?? '', + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CANCELLED: + return translateLocal('travel.updates.flightSeatCancelled', { + airlineCode: details.route?.airlineCode ?? '', + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.PAYMENT_DECLINED: + return translateLocal('travel.updates.paymentDeclined'); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_TRAVELER: + return translateLocal('travel.updates.bookingCancelledByTraveller', { + type: details.type, + id: details.reservationID, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_VENDOR: + return translateLocal('travel.updates.bookingCancelledByVendor', { + type: details.type, + id: details.reservationID, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_REBOOKED: + return translateLocal('travel.updates.bookingRebooked', { + type: details.type, + id: details.confirmations?.at(0)?.value, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_UPDATED: + return translateLocal('travel.updates.bookingUpdated', { + type: details.type, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TRIP_UPDATED: + if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { + return translateLocal('travel.updates.defaultUpdate', { + type: details.type, + }); + } + if (details.type === CONST.RESERVATION_TYPE.TRAIN) { + return translateLocal('travel.updates.railTicketUpdate', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: formattedStartDate, + }); + } + return translateLocal('travel.updates.flightChanged', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_OTHER_UPDATE: + if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { + return translateLocal('travel.updates.defaultUpdate', { + type: details.type, + }); + } + if (details.type === CONST.RESERVATION_TYPE.TRAIN) { + return translateLocal('travel.updates.railTicketUpdate', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: formattedStartDate, + }); + } + return translateLocal('travel.updates.flightChanged', { + airlineCode: details.route?.airlineCode ?? '', + origin: details.start.shortName ?? '', + destination: details.end?.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.REFUND: + return translateLocal('travel.updates.railTicketRefund', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: formattedStartDate, + }); + + case CONST.TRAVEL.UPDATE_OPERATION_TYPE.EXCHANGE: + return translateLocal('travel.updates.railTicketExchange', { + origin: details.start.cityName ?? details.start.shortName ?? '', + destination: details.end.cityName ?? details.end.shortName ?? '', + startDate: formattedStartDate, + }); + + default: + return 'Trip was updated'; + } +} + function getMemberChangeMessageFragment(reportAction: OnyxEntry, getReportNameCallback: typeof getReportName): Message { const messageElements: readonly MemberChangeMessageElement[] = getMemberChangeMessageElements(reportAction, getReportNameCallback); const html = messageElements @@ -1579,6 +1742,11 @@ function getReportActionMessageFragments(action: ReportAction): Message[] { return [{text: message, html: `${message}`, type: 'COMMENT'}]; } + if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { + const message = getTravelUpdateMessage(action); + return [{text: message, html: `${message}`, type: 'COMMENT'}]; + } + if (isConciergeCategoryOptions(action)) { const message = getReportActionMessageText(action); return [{text: message, html: message, type: 'COMMENT'}]; @@ -2570,6 +2738,7 @@ export { wasMessageReceivedWhileOffline, shouldShowAddMissingDetails, getJoinRequestMessage, + getTravelUpdateMessage, getWorkspaceCategoryUpdateMessage, getWorkspaceUpdateFieldMessage, getWorkspaceCurrencyUpdateMessage, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 271ad80839b6..1c67d4138fdf 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -17,7 +17,6 @@ import type { OriginalMessageIOU, OriginalMessageModifiedExpense, OriginalMessageMovedTransaction, - OriginalMessageTravelUpdate, } from 'src/types/onyx/OriginalMessage'; import type {SetRequired, TupleToUnion, ValueOf} from 'type-fest'; import type {FileObject} from '@components/AttachmentModal'; @@ -163,6 +162,7 @@ import { getReportActionMessageText, getReportActionText, getRetractedMessage, + getTravelUpdateMessage, getWorkspaceCurrencyUpdateMessage, getWorkspaceFrequencyUpdateMessage, getWorkspaceReportFieldAddMessage, @@ -4868,6 +4868,10 @@ function getReportNameInternal({ return getIntegrationSyncFailedMessage(parentReportAction); } + if(isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)){ + return getTravelUpdateMessage(parentReportAction) + } + if (isChatThread(report)) { if (!isEmptyObject(parentReportAction) && isTransactionThread(parentReportAction)) { formattedName = getTransactionReportName({reportAction: parentReportAction, transactions, reports}); @@ -5888,167 +5892,6 @@ function getPolicyChangeMessage(action: ReportAction) { }); return message; } -function getTravelUpdateMessage(action: ReportAction, formatDate: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { - const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; - const formattedStartDate = formatDate(details.start.date, false); - - switch (details?.operation) { - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: - return translateLocal('travel.updates.bookingTicketed', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - confirmationID: details.confirmations?.at(0)?.value, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_VOIDED: - return translateLocal('travel.updates.ticketVoided', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TICKET_REFUNDED: - return translateLocal('travel.updates.ticketRefunded', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CANCELLED: - return translateLocal('travel.updates.flightCancelled', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_PENDING: - return translateLocal('travel.updates.flightScheduleChangePending', { - airlineCode: details.route?.airlineCode ?? '', - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SCHEDULE_CHANGE_CLOSED: - return translateLocal('travel.updates.flightScheduleChangeClosed', { - airlineCode: details.route?.airlineCode ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CHANGED: - return translateLocal('travel.updates.flightChanged', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CABIN_CHANGED: - return translateLocal('travel.updates.flightCabinChanged', { - airlineCode: details.route?.airlineCode ?? '', - cabinClass: details.route?.class ?? '', - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CONFIRMED: - return translateLocal('travel.updates.flightSeatConfirmed', { - airlineCode: details.route?.airlineCode ?? '', - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CHANGED: - return translateLocal('travel.updates.flightSeatChanged', { - airlineCode: details.route?.airlineCode ?? '', - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_SEAT_CANCELLED: - return translateLocal('travel.updates.flightSeatCancelled', { - airlineCode: details.route?.airlineCode ?? '', - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.PAYMENT_DECLINED: - return translateLocal('travel.updates.paymentDeclined'); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_TRAVELER: - return translateLocal('travel.updates.bookingCancelledByTraveller', { - type: details.type, - id: details.reservationID, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_VENDOR: - return translateLocal('travel.updates.bookingCancelledByVendor', { - type: details.type, - id: details.reservationID, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_REBOOKED: - return translateLocal('travel.updates.bookingRebooked', { - type: details.type, - id: details.confirmations?.at(0)?.value, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_UPDATED: - return translateLocal('travel.updates.bookingUpdated', { - type: details.type, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.TRIP_UPDATED: - if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { - return translateLocal('travel.updates.defaultUpdate', { - type: details.type, - }); - } - if (details.type === CONST.RESERVATION_TYPE.TRAIN) { - return translateLocal('travel.updates.railTicketUpdate', { - origin: details.start.cityName ?? details.start.shortName ?? '', - destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: formattedStartDate, - }); - } - return translateLocal('travel.updates.flightChanged', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_OTHER_UPDATE: - if (details.type === CONST.RESERVATION_TYPE.CAR || details.type === CONST.RESERVATION_TYPE.HOTEL) { - return translateLocal('travel.updates.defaultUpdate', { - type: details.type, - }); - } - if (details.type === CONST.RESERVATION_TYPE.TRAIN) { - return translateLocal('travel.updates.railTicketUpdate', { - origin: details.start.cityName ?? details.start.shortName ?? '', - destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: formattedStartDate, - }); - } - return translateLocal('travel.updates.flightChanged', { - airlineCode: details.route?.airlineCode ?? '', - origin: details.start.shortName ?? '', - destination: details.end?.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.REFUND: - return translateLocal('travel.updates.railTicketRefund', { - origin: details.start.cityName ?? details.start.shortName ?? '', - destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: formattedStartDate, - }); - - case CONST.TRAVEL.UPDATE_OPERATION_TYPE.EXCHANGE: - return translateLocal('travel.updates.railTicketExchange', { - origin: details.start.cityName ?? details.start.shortName ?? '', - destination: details.end.cityName ?? details.end.shortName ?? '', - startDate: formattedStartDate, - }); - - default: - return 'Unknown trip update'; - } -} /** * @param iouReportID - the report ID of the IOU report the action belongs to @@ -11118,7 +10961,6 @@ export { getReimbursementDeQueuedOrCanceledActionMessage, getReimbursementQueuedActionMessage, getReportActionActorAccountID, - getTravelUpdateMessage, getReportDescription, getReportFieldKey, getReportIDFromLink, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 936851e731fa..d9b9489fbd2b 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -46,6 +46,7 @@ import { getReportActionMessageText, getRetractedMessage, getSortedReportActions, + getTravelUpdateMessage, getWorkspaceCategoryUpdateMessage, getWorkspaceCurrencyUpdateMessage, getWorkspaceCustomUnitRateAddedMessage, @@ -663,6 +664,8 @@ function getOptionData({ result.alternateText = getRetractedMessage(); } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REOPENED) { result.alternateText = getReopenedMessage(); + } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) { + result.alternateText = getTravelUpdateMessage(lastAction); } else { result.alternateText = lastMessageTextFromReport.length > 0 diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx index 0d1aa6423752..406afc260445 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx @@ -42,6 +42,7 @@ import { getRenamedAction, getReopenedMessage, getReportActionMessageText, + getTravelUpdateMessage, getUpdateRoomDescriptionMessage, getWorkspaceCategoryUpdateMessage, getWorkspaceCurrencyUpdateMessage, @@ -626,6 +627,8 @@ const ContextMenuActions: ContextMenuAction[] = [ setClipboardMessage(getCardIssuedMessage({reportAction, shouldRenderHTML: true, policyID: report?.policyID, card})); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_INTEGRATION)) { setClipboardMessage(getRemovedConnectionMessage(reportAction)); + } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { + setClipboardMessage(getTravelUpdateMessage(reportAction)); } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CHANGE_POLICY) { const displayMessage = getPolicyChangeMessage(reportAction); Clipboard.setString(displayMessage); diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index 40744509df1e..1ed981f461cf 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -77,6 +77,7 @@ import { getReopenedMessage, getReportActionMessage, getReportActionText, + getTravelUpdateMessage, getWhisperedTo, getWorkspaceCategoryUpdateMessage, getWorkspaceCurrencyUpdateMessage, @@ -129,7 +130,6 @@ import { getMovedTransactionMessage, getPolicyChangeMessage, getRejectedReportMessage, - getTravelUpdateMessage, getUpgradeWorkspaceMessage, getWhisperDisplayNames, getWorkspaceNameUpdatedMessage, From 4a771c7ecc1ed2c3183834fdc2b00d7d81acb5e0 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 23:03:38 +0200 Subject: [PATCH 12/19] Adjust spelling --- cspell.json | 2 ++ src/languages/en.ts | 2 +- src/libs/ReportActionsUtils.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cspell.json b/cspell.json index ff8467bdb2f3..06e2ff544433 100644 --- a/cspell.json +++ b/cspell.json @@ -461,6 +461,8 @@ "reactnativekeycommand", "reauthentication", "Reauthenticator", + "Rebooked", + "REBOOKED", "recategorize", "recents", "REDIRECTURI", diff --git a/src/languages/en.ts b/src/languages/en.ts index cff82cf12b81..5b1f01c03658 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2965,7 +2965,7 @@ const translations = { flightSeatChanged: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} has been changed.`, flightSeatCancelled: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} was removed.`, paymentDeclined: 'Payment for your air booking failed. Please try again.', - bookingCancelledByTraveller: ({type, id = ''}: TravelTypeParams) => `You cancelled your ${type} reservation ${id}.`, + bookingCancelledByTraveler: ({type, id = ''}: TravelTypeParams) => `You cancelled your ${type} reservation ${id}.`, bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `The vendor cancelled your ${type} reservation ${id}.`, bookingRebooked: ({type, id = ''}: TravelTypeParams) => `Your ${type} reservation was re-booked. New confirmation #:${id}.`, bookingUpdated: ({type}: TravelTypeParams) => `Your ${type} booking was updated. Review the new details in the itinerary.`, diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 464591c8808b..20f93aa17592 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1591,7 +1591,7 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st return translateLocal('travel.updates.paymentDeclined'); case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_CANCELED_BY_TRAVELER: - return translateLocal('travel.updates.bookingCancelledByTraveller', { + return translateLocal('travel.updates.bookingCancelledByTraveler', { type: details.type, id: details.reservationID, }); From 3a8a16d04623d961003aa200a0a67c935f71e113 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 23:07:59 +0200 Subject: [PATCH 13/19] Fix prettier --- src/libs/ReportUtils.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1c67d4138fdf..8b2dbbf299c2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1320,7 +1320,7 @@ function isExpenseReport(report: OnyxInputOrEntry | SearchReport): boole * Checks if a report is an IOU report using report or reportID */ function isIOUReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { - const report = typeof reportOrID === 'string' ? (getReport(reportOrID, allReports) ?? null) : reportOrID; + const report = typeof reportOrID === 'string' ? getReport(reportOrID, allReports) ?? null : reportOrID; return report?.type === CONST.REPORT.TYPE.IOU; } @@ -1431,7 +1431,7 @@ function isSettled(reportOrID: OnyxInputOrEntry | SearchReport | string if (!reportOrID) { return false; } - const report = typeof reportOrID === 'string' ? (getReport(reportOrID, reports ?? allReports) ?? null) : reportOrID; + const report = typeof reportOrID === 'string' ? getReport(reportOrID, reports ?? allReports) ?? null : reportOrID; if (!report) { return false; } @@ -2152,7 +2152,7 @@ function isTrackExpenseReport(report: OnyxInputOrEntry): boolean { * Checks if a report is an IOU or expense request. */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { - const report = typeof reportOrID === 'string' ? (getReport(reportOrID, allReports) ?? null) : reportOrID; + const report = typeof reportOrID === 'string' ? getReport(reportOrID, allReports) ?? null : reportOrID; return isIOURequest(report) || isExpenseRequest(report); } @@ -2160,7 +2160,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { * Checks if a report is an IOU or expense report. */ function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string, reports?: SearchReport[]): boolean { - const report = typeof reportOrID === 'string' ? (getReport(reportOrID, reports ?? allReports) ?? null) : reportOrID; + const report = typeof reportOrID === 'string' ? getReport(reportOrID, reports ?? allReports) ?? null : reportOrID; return isIOUReport(report) || isExpenseReport(report); } @@ -3902,7 +3902,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry } const iouMessage = getOriginalMessage(reportAction); - const moneyRequestReport = iouMessage?.IOUReportID ? (getReport(iouMessage?.IOUReportID, allReports) ?? ({} as Report)) : ({} as Report); + const moneyRequestReport = iouMessage?.IOUReportID ? getReport(iouMessage?.IOUReportID, allReports) ?? ({} as Report) : ({} as Report); const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouMessage?.IOUTransactionID}`] ?? ({} as Transaction); if (isSettled(String(moneyRequestReport.reportID)) || isReportIDApproved(String(moneyRequestReport.reportID))) { @@ -4758,7 +4758,7 @@ function getReportNameInternal({ isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(parentReportAction) ) { - const harvesting = !isMarkAsClosedAction(parentReportAction) ? (getOriginalMessage(parentReportAction)?.harvesting ?? false) : false; + const harvesting = !isMarkAsClosedAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.harvesting ?? false : false; if (harvesting) { return translateLocal('iou.automaticallySubmitted'); } @@ -4868,8 +4868,8 @@ function getReportNameInternal({ return getIntegrationSyncFailedMessage(parentReportAction); } - if(isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)){ - return getTravelUpdateMessage(parentReportAction) + if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { + return getTravelUpdateMessage(parentReportAction); } if (isChatThread(report)) { @@ -8672,7 +8672,7 @@ function getOriginalReportID(reportID: string | undefined, reportAction: OnyxInp const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? ([] as ReportAction[])); const isThreadReportParentAction = reportAction?.childReportID?.toString() === reportID; if (Object.keys(currentReportAction ?? {}).length === 0) { - return isThreadReportParentAction ? getReport(reportID, allReports)?.parentReportID : (transactionThreadReportID ?? reportID); + return isThreadReportParentAction ? getReport(reportID, allReports)?.parentReportID : transactionThreadReportID ?? reportID; } return reportID; } @@ -9836,7 +9836,7 @@ function prepareOnboardingOnyxData( !currentUserEmail?.includes('+'); const adminsChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = shouldPostTasksInAdminsRoom - ? (adminsChatReport ?? {reportID: adminsChatReportID, policyID: onboardingPolicyID}) + ? adminsChatReport ?? {reportID: adminsChatReportID, policyID: onboardingPolicyID} : getChatByParticipants([CONST.ACCOUNT_ID.CONCIERGE, currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID], allReports, false, true); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -9934,7 +9934,7 @@ function prepareOnboardingOnyxData( task.mediaAttributes, ); const emailCreatingAction = - engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? (allPersonalDetails?.[actorAccountID]?.login ?? CONST.EMAIL.CONCIERGE) : CONST.EMAIL.CONCIERGE; + engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? allPersonalDetails?.[actorAccountID]?.login ?? CONST.EMAIL.CONCIERGE : CONST.EMAIL.CONCIERGE; const taskCreatedAction = buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = buildOptimisticTaskCommentReportAction(currentTask.reportID, taskTitle, 0, `task for ${taskTitle}`, targetChatReportID, actorAccountID, index + 3); currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; @@ -10396,7 +10396,7 @@ function isChatUsedForOnboarding(optionOrReport: OnyxEntry | OptionData, // Guides and onboarding tasks are posted to the #admins room to facilitate the onboarding process. return onboardingPurposeSelected === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+') ? isAdminRoom(optionOrReport) - : ((optionOrReport as OptionData)?.isConciergeChat ?? isConciergeChatReport(optionOrReport)); + : (optionOrReport as OptionData)?.isConciergeChat ?? isConciergeChatReport(optionOrReport); } /** @@ -10488,7 +10488,7 @@ function getReportLastVisibleActionCreated(report: OnyxEntry, oneTransac } function getSourceIDFromReportAction(reportAction: OnyxEntry): string { - const message = Array.isArray(reportAction?.message) ? (reportAction?.message?.at(-1) ?? null) : (reportAction?.message ?? null); + const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null; const html = message?.html ?? ''; const {sourceURL} = getAttachmentDetails(html); const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; From 61471410d3b7e0fd0a1c9a021819b7793cc2520b Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 23:15:28 +0200 Subject: [PATCH 14/19] Fix typecheck --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index e0fbc9369d1f..a44e4af18616 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2989,7 +2989,7 @@ const translations = { flightSeatChanged: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido modificada.`, flightSeatCancelled: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} fue eliminada.`, paymentDeclined: 'El pago de tu reserva aérea ha fallado. Por favor, inténtalo de nuevo.', - bookingCancelledByTraveller: ({type, id = ''}: TravelTypeParams) => `Cancelaste tu reserva de ${type} ${id}.`, + bookingCancelledByTraveler: ({type, id = ''}: TravelTypeParams) => `Cancelaste tu reserva de ${type} ${id}.`, bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `El proveedor canceló tu reserva de ${type} ${id}.`, bookingRebooked: ({type, id = ''}: TravelTypeParams) => `Tu reserva de ${type} fue reprogramada. Nuevo número de confirmación: ${id}.`, bookingUpdated: ({type}: TravelTypeParams) => `Tu reserva de ${type} fue actualizada. Revisa los nuevos detalles en el itinerario.`, From 636cb53e7b41c622dd4dbc7ea0abd66a03aaf30a Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 23:46:06 +0200 Subject: [PATCH 15/19] Fix prettier --- Mobile-Expensify | 2 +- src/libs/OptionsListUtils.ts | 12 ++++++------ src/libs/ReportUtils.ts | 22 +++++++++++----------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 2ce0d843bcd7..3821cc5ea7d8 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 2ce0d843bcd793ae2ca781c24ceabae03430d699 +Subproject commit 3821cc5ea7d8babee4cd44259f58f1380ece9fa1 diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index a4cd225aa007..4c1e5922f860 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -631,7 +631,7 @@ function getAlternateText(option: OptionData, {showChatPreviewLine = false, forc return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageTextWithPrefix - : formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); + : formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? (option.participantsList.at(0)?.login ?? '') : ''); } /** @@ -773,7 +773,7 @@ function getLastMessageTextForReport( isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(lastReportAction) ) { - const wasSubmittedViaHarvesting = !isMarkAsClosedAction(lastReportAction) ? getOriginalMessage(lastReportAction)?.harvesting ?? false : false; + const wasSubmittedViaHarvesting = !isMarkAsClosedAction(lastReportAction) ? (getOriginalMessage(lastReportAction)?.harvesting ?? false) : false; if (wasSubmittedViaHarvesting) { lastMessageTextFromReport = translateLocal('iou.automaticallySubmitted'); } else { @@ -913,7 +913,7 @@ function createOption(accountIDs: number[], personalDetails: OnyxInputOrEntry iouAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); if (lastIOUAction) { lastIOUCreationDate = lastIOUAction.lastModified; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index a32f4c8440cf..84f0ef64df0c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1326,7 +1326,7 @@ function isExpenseReport(report: OnyxInputOrEntry | SearchReport): boole * Checks if a report is an IOU report using report or reportID */ function isIOUReport(reportOrID: OnyxInputOrEntry | SearchReport | string): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID, allReports) ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? (getReport(reportOrID, allReports) ?? null) : reportOrID; return report?.type === CONST.REPORT.TYPE.IOU; } @@ -1437,7 +1437,7 @@ function isSettled(reportOrID: OnyxInputOrEntry | SearchReport | string if (!reportOrID) { return false; } - const report = typeof reportOrID === 'string' ? getReport(reportOrID, reports ?? allReports) ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? (getReport(reportOrID, reports ?? allReports) ?? null) : reportOrID; if (!report) { return false; } @@ -2158,7 +2158,7 @@ function isTrackExpenseReport(report: OnyxInputOrEntry): boolean { * Checks if a report is an IOU or expense request. */ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID, allReports) ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? (getReport(reportOrID, allReports) ?? null) : reportOrID; return isIOURequest(report) || isExpenseRequest(report); } @@ -2166,7 +2166,7 @@ function isMoneyRequest(reportOrID: OnyxEntry | string): boolean { * Checks if a report is an IOU or expense report. */ function isMoneyRequestReport(reportOrID: OnyxInputOrEntry | SearchReport | string, reports?: SearchReport[]): boolean { - const report = typeof reportOrID === 'string' ? getReport(reportOrID, reports ?? allReports) ?? null : reportOrID; + const report = typeof reportOrID === 'string' ? (getReport(reportOrID, reports ?? allReports) ?? null) : reportOrID; return isIOUReport(report) || isExpenseReport(report); } @@ -3904,7 +3904,7 @@ function canEditFieldOfMoneyRequest(reportAction: OnyxInputOrEntry } const iouMessage = getOriginalMessage(reportAction); - const moneyRequestReport = iouMessage?.IOUReportID ? getReport(iouMessage?.IOUReportID, allReports) ?? ({} as Report) : ({} as Report); + const moneyRequestReport = iouMessage?.IOUReportID ? (getReport(iouMessage?.IOUReportID, allReports) ?? ({} as Report)) : ({} as Report); const transaction = allTransactions?.[`${ONYXKEYS.COLLECTION.TRANSACTION}${iouMessage?.IOUTransactionID}`] ?? ({} as Transaction); if (isSettled(String(moneyRequestReport.reportID)) || isReportIDApproved(String(moneyRequestReport.reportID))) { @@ -4759,7 +4759,7 @@ function getReportNameInternal({ isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) || isMarkAsClosedAction(parentReportAction) ) { - const harvesting = !isMarkAsClosedAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.harvesting ?? false : false; + const harvesting = !isMarkAsClosedAction(parentReportAction) ? (getOriginalMessage(parentReportAction)?.harvesting ?? false) : false; if (harvesting) { return translateLocal('iou.automaticallySubmitted'); } @@ -8688,7 +8688,7 @@ function getOriginalReportID(reportID: string | undefined, reportAction: OnyxInp const transactionThreadReportID = getOneTransactionThreadReportID(reportID, reportActions ?? ([] as ReportAction[])); const isThreadReportParentAction = reportAction?.childReportID?.toString() === reportID; if (Object.keys(currentReportAction ?? {}).length === 0) { - return isThreadReportParentAction ? getReport(reportID, allReports)?.parentReportID : transactionThreadReportID ?? reportID; + return isThreadReportParentAction ? getReport(reportID, allReports)?.parentReportID : (transactionThreadReportID ?? reportID); } return reportID; } @@ -9852,7 +9852,7 @@ function prepareOnboardingOnyxData( !currentUserEmail?.includes('+'); const adminsChatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`]; const targetChatReport = shouldPostTasksInAdminsRoom - ? adminsChatReport ?? {reportID: adminsChatReportID, policyID: onboardingPolicyID} + ? (adminsChatReport ?? {reportID: adminsChatReportID, policyID: onboardingPolicyID}) : getChatByParticipants([CONST.ACCOUNT_ID.CONCIERGE, currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID], allReports, false, true); const {reportID: targetChatReportID = '', policyID: targetChatPolicyID = ''} = targetChatReport ?? {}; @@ -9950,7 +9950,7 @@ function prepareOnboardingOnyxData( task.mediaAttributes, ); const emailCreatingAction = - engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? allPersonalDetails?.[actorAccountID]?.login ?? CONST.EMAIL.CONCIERGE : CONST.EMAIL.CONCIERGE; + engagementChoice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM ? (allPersonalDetails?.[actorAccountID]?.login ?? CONST.EMAIL.CONCIERGE) : CONST.EMAIL.CONCIERGE; const taskCreatedAction = buildOptimisticCreatedReportAction(emailCreatingAction); const taskReportAction = buildOptimisticTaskCommentReportAction(currentTask.reportID, taskTitle, 0, `task for ${taskTitle}`, targetChatReportID, actorAccountID, index + 3); currentTask.parentReportActionID = taskReportAction.reportAction.reportActionID; @@ -10412,7 +10412,7 @@ function isChatUsedForOnboarding(optionOrReport: OnyxEntry | OptionData, // Guides and onboarding tasks are posted to the #admins room to facilitate the onboarding process. return onboardingPurposeSelected === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !currentUserEmail?.includes('+') ? isAdminRoom(optionOrReport) - : (optionOrReport as OptionData)?.isConciergeChat ?? isConciergeChatReport(optionOrReport); + : ((optionOrReport as OptionData)?.isConciergeChat ?? isConciergeChatReport(optionOrReport)); } /** @@ -10504,7 +10504,7 @@ function getReportLastVisibleActionCreated(report: OnyxEntry, oneTransac } function getSourceIDFromReportAction(reportAction: OnyxEntry): string { - const message = Array.isArray(reportAction?.message) ? reportAction?.message?.at(-1) ?? null : reportAction?.message ?? null; + const message = Array.isArray(reportAction?.message) ? (reportAction?.message?.at(-1) ?? null) : (reportAction?.message ?? null); const html = message?.html ?? ''; const {sourceURL} = getAttachmentDetails(html); const sourceID = (sourceURL?.match(CONST.REGEX.ATTACHMENT_ID) ?? [])[1]; From 3aadbc9f97ded51fc1fa201f18b8a27071ea28ee Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Tue, 3 Jun 2025 23:47:16 +0200 Subject: [PATCH 16/19] Update Mobile-Expensify version --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 3821cc5ea7d8..2ce0d843bcd7 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 3821cc5ea7d8babee4cd44259f58f1380ece9fa1 +Subproject commit 2ce0d843bcd793ae2ca781c24ceabae03430d699 From 80ca4410759f51c0badf922766233012d017080d Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Fri, 6 Jun 2025 15:16:08 +0200 Subject: [PATCH 17/19] Adjust to review comments --- src/languages/en.ts | 5 +++-- src/languages/es.ts | 1 + src/libs/ReportActionsUtils.ts | 12 +++++++++--- src/types/onyx/OriginalMessage.ts | 1 - 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index c48f76d7f583..5cd69169563f 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2955,9 +2955,9 @@ const translations = { ticketVoided: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been voided.`, ticketRefunded: ({airlineCode, origin, destination, startDate}: FlightParams) => - `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been refunded or exchange.`, + `Your ticket for flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been refunded or exchanged.`, flightCancelled: ({airlineCode, origin, destination, startDate}: FlightParams) => - `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate}} has been canceled by the airline`, + `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate}} has been canceled by the airline.`, flightScheduleChangePending: ({airlineCode}: AirlineParams) => `The airline has proposed a schedule change for flight ${airlineCode}; we are awaiting confirmation.`, flightScheduleChangeClosed: ({airlineCode, startDate}: AirlineParams) => `Schedule change confirmed: flight ${airlineCode} now departs at ${startDate}.`, flightChanged: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been changed.`, @@ -2975,6 +2975,7 @@ const translations = { railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been exchanged.`, railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been updated.`, defaultUpdate: ({type}: TravelTypeParams) => `Your ${type} reservation was updated.`, + notSupported: 'Not supported travel update.', }, }, workspace: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 9afc42729581..2886539d4b25 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2999,6 +2999,7 @@ const translations = { railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido cambiado.`, railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido actualizado.`, defaultUpdate: ({type}: TravelTypeParams) => `Tu reserva de ${type} fue actualizada.`, + notSupported: 'Actualización de viaje no admitida.', }, }, workspace: { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index d752aafebafb..7533766c8bed 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -12,7 +12,7 @@ import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Card, Locale, OnyxInputOrEntry, OriginalMessageIOU, PrivatePersonalDetails} from '@src/types/onyx'; -import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration, OriginalMessageTravelUpdate} from '@src/types/onyx/OriginalMessage'; +import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration} from '@src/types/onyx/OriginalMessage'; import type {PolicyReportFieldType} from '@src/types/onyx/Policy'; import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; @@ -1531,7 +1531,11 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD } function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { - const details = getOriginalMessage(action) as OriginalMessageTravelUpdate; + const details = isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) ? getOriginalMessage(action) : undefined; + if (!details) { + return translateLocal('travel.updates.notSupported'); + } + const formattedStartDate = formatDate?.(details.start.date, false) ?? format(details.start.date, CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); switch (details?.operation) { @@ -1688,7 +1692,9 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st }); default: - return 'Trip was updated'; + return translateLocal('travel.updates.defaultUpdate', { + type: details.type, + }); } } diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 151fcfe9e988..759ea2c5ea0e 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -869,5 +869,4 @@ export type { OriginalMessageChangePolicy, OriginalMessageUnreportedTransaction, OriginalMessageMovedTransaction, - OriginalMessageTravelUpdate, }; From f34df07852d5a0bb2f42e653ff925518c4e99447 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Wed, 11 Jun 2025 23:13:23 +0200 Subject: [PATCH 18/19] Adjusted to review --- Mobile-Expensify | 2 +- src/languages/en.ts | 5 ++--- src/languages/es.ts | 3 +-- src/libs/ReportActionsUtils.ts | 18 +++++++----------- src/libs/SidebarUtils.ts | 2 +- src/pages/home/report/PureReportActionItem.tsx | 2 +- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 34059d19ec5c..2ce0d843bcd7 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 34059d19ec5c4660160440dc5bd4ad65aa9c4be7 +Subproject commit 2ce0d843bcd793ae2ca781c24ceabae03430d699 diff --git a/src/languages/en.ts b/src/languages/en.ts index f6fda6c1c72f..8a4090cd2171 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3012,14 +3012,14 @@ const translations = { `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate}} has been canceled by the airline.`, flightScheduleChangePending: ({airlineCode}: AirlineParams) => `The airline has proposed a schedule change for flight ${airlineCode}; we are awaiting confirmation.`, flightScheduleChangeClosed: ({airlineCode, startDate}: AirlineParams) => `Schedule change confirmed: flight ${airlineCode} now departs at ${startDate}.`, - flightChanged: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been changed.`, + flightUpdated: ({airlineCode, origin, destination, startDate}: FlightParams) => `Your flight ${airlineCode} (${origin} → ${destination}) on ${startDate} has been updated.`, flightCabinChanged: ({airlineCode, cabinClass}: AirlineParams) => `Your cabin class has been updated to ${cabinClass} on flight ${airlineCode}.`, flightSeatConfirmed: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} has been confirmed.`, flightSeatChanged: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} has been changed.`, flightSeatCancelled: ({airlineCode}: AirlineParams) => `Your seat assignment on flight ${airlineCode} was removed.`, paymentDeclined: 'Payment for your air booking failed. Please try again.', bookingCancelledByTraveler: ({type, id = ''}: TravelTypeParams) => `You cancelled your ${type} reservation ${id}.`, - bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `The vendor cancelled your ${type} reservation ${id}.`, + bookingCancelledByVendor: ({type, id = ''}: TravelTypeParams) => `The vendor cancelled your ${type} reservation ${id}.`, bookingRebooked: ({type, id = ''}: TravelTypeParams) => `Your ${type} reservation was re-booked. New confirmation #:${id}.`, bookingUpdated: ({type}: TravelTypeParams) => `Your ${type} booking was updated. Review the new details in the itinerary.`, railTicketRefund: ({origin, destination, startDate}: RailTicketParams) => @@ -3027,7 +3027,6 @@ const translations = { railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been exchanged.`, railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Your rail ticket for ${origin} → ${destination} on ${startDate} has been updated.`, defaultUpdate: ({type}: TravelTypeParams) => `Your ${type} reservation was updated.`, - notSupported: 'Not supported travel update.', }, }, workspace: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 5114b424a886..efe27daad396 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -3036,7 +3036,7 @@ const translations = { `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido cancelado por la aerolínea.`, flightScheduleChangePending: ({airlineCode}: AirlineParams) => `La aerolínea ha propuesto un cambio de horario para el vuelo ${airlineCode}; estamos esperando la confirmación.`, flightScheduleChangeClosed: ({airlineCode, startDate}: AirlineParams) => `Cambio de horario confirmado: el vuelo ${airlineCode} ahora sale a las ${startDate}.`, - flightChanged: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido modificado.`, + flightUpdated: ({airlineCode, origin, destination, startDate}: FlightParams) => `Tu vuelo ${airlineCode} (${origin} → ${destination}) del ${startDate} ha sido actualizado.`, flightCabinChanged: ({airlineCode, cabinClass}: AirlineParams) => `Tu clase de cabina ha sido actualizada a ${cabinClass} en el vuelo ${airlineCode}.`, flightSeatConfirmed: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido confirmada.`, flightSeatChanged: ({airlineCode}: AirlineParams) => `Tu asignación de asiento en el vuelo ${airlineCode} ha sido modificada.`, @@ -3051,7 +3051,6 @@ const translations = { railTicketExchange: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido cambiado.`, railTicketUpdate: ({origin, destination, startDate}: RailTicketParams) => `Tu billete de tren de ${origin} a ${destination} para el ${startDate} ha sido actualizado.`, defaultUpdate: ({type}: TravelTypeParams) => `Tu reserva de ${type} fue actualizada.`, - notSupported: 'Actualización de viaje no admitida.', }, }, workspace: { diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 72e835104cff..3b7d7b71c92c 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1565,13 +1565,9 @@ function getMessageOfOldDotReportAction(oldDotAction: PartialReportAction | OldD } } -function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { - const details = isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) ? getOriginalMessage(action) : undefined; - if (!details) { - return translateLocal('travel.updates.notSupported'); - } - - const formattedStartDate = formatDate?.(details.start.date, false) ?? format(details.start.date, CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); +function getTravelUpdateMessage(action: ReportAction<'TRAVEL_TRIP_ROOM_UPDATE'>, formatDate?: (datetime: string, includeTimezone: boolean, isLowercase?: boolean | undefined) => string) { + const details = getOriginalMessage(action); + const formattedStartDate = formatDate?.(details?.start.date ?? '', false) ?? format(details?.start.date ?? '', CONST.DATE.FNS_DATE_TIME_FORMAT_STRING); switch (details?.operation) { case CONST.TRAVEL.UPDATE_OPERATION_TYPE.BOOKING_TICKETED: @@ -1619,7 +1615,7 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st }); case CONST.TRAVEL.UPDATE_OPERATION_TYPE.FLIGHT_CHANGED: - return translateLocal('travel.updates.flightChanged', { + return translateLocal('travel.updates.flightUpdated', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', @@ -1686,7 +1682,7 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st startDate: formattedStartDate, }); } - return translateLocal('travel.updates.flightChanged', { + return translateLocal('travel.updates.flightUpdated', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', @@ -1705,7 +1701,7 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st startDate: formattedStartDate, }); } - return translateLocal('travel.updates.flightChanged', { + return translateLocal('travel.updates.flightUpdated', { airlineCode: details.route?.airlineCode ?? '', origin: details.start.shortName ?? '', destination: details.end?.shortName ?? '', @@ -1728,7 +1724,7 @@ function getTravelUpdateMessage(action: ReportAction, formatDate?: (datetime: st default: return translateLocal('travel.updates.defaultUpdate', { - type: details.type, + type: details?.type ?? '', }); } } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 04606f36b051..decb57685d40 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -701,7 +701,7 @@ function getOptionData({ result.alternateText = getRetractedMessage(); } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REOPENED) { result.alternateText = getReopenedMessage(); - } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) { + } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { result.alternateText = getTravelUpdateMessage(lastAction); } else { result.alternateText = diff --git a/src/pages/home/report/PureReportActionItem.tsx b/src/pages/home/report/PureReportActionItem.tsx index d09aaac3e559..ff3b7fefad52 100644 --- a/src/pages/home/report/PureReportActionItem.tsx +++ b/src/pages/home/report/PureReportActionItem.tsx @@ -1067,7 +1067,7 @@ function PureReportActionItem({ ${getMovedTransactionMessage(action)}`} /> ); - } else if (action.actionName === CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE) { + } else if (isActionOfType(action, CONST.REPORT.ACTIONS.TYPE.TRAVEL_UPDATE)) { children = ( ${getTravelUpdateMessage(action, datetimeToCalendarTime)}`} /> From 383e2b317322f6fd25e1b4b9b6ecb0b828110365 Mon Sep 17 00:00:00 2001 From: Zuzanna Furtak Date: Wed, 11 Jun 2025 23:16:14 +0200 Subject: [PATCH 19/19] Update Mobile-Expensify version --- Mobile-Expensify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mobile-Expensify b/Mobile-Expensify index 2ce0d843bcd7..34059d19ec5c 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit 2ce0d843bcd793ae2ca781c24ceabae03430d699 +Subproject commit 34059d19ec5c4660160440dc5bd4ad65aa9c4be7