Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/trip details #52823

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4386,7 +4386,7 @@ const CONST = {
BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page',
TRAVEL_DOT_URL: 'https://travel.expensify.com',
STAGING_TRAVEL_DOT_URL: 'https://staging.travel.expensify.com',
TRIP_ID_PATH: (tripID: string) => `trips/${tripID}`,
TRIP_ID_PATH: (tripID?: string) => (tripID ? `trips/${tripID}` : undefined),
SPOTNANA_TMC_ID: '8e8e7258-1cf3-48c0-9cd1-fe78a6e31eed',
STAGING_SPOTNANA_TMC_ID: '7a290c6e-5328-4107-aff6-e48765845b81',
SCREEN_READER_STATES: {
Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,10 @@ const ROUTES = {
TRAVEL_MY_TRIPS: 'travel',
TRAVEL_TCS: 'travel/terms',
TRACK_TRAINING_MODAL: 'track-training',
TRAVEL_TRIP_DETAILS: {
route: 'r/:reportID/trip/:transactionID',
getRoute: (reportID: string, transactionID: string, backTo?: string) => getUrlWithBackToParam(`r/${reportID}/trip/${transactionID}`, backTo),
},
ONBOARDING_ROOT: {
route: 'onboarding',
getRoute: (backTo?: string) => getUrlWithBackToParam(`onboarding`, backTo),
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const SCREENS = {
TRAVEL: {
MY_TRIPS: 'Travel_MyTrips',
TCS: 'Travel_TCS',
TRIP_DETAILS: 'Trip_Details',
},
SEARCH: {
CENTRAL_PANE: 'Search_Central_Pane',
Expand Down
34 changes: 30 additions & 4 deletions src/components/ApprovalWorkflowSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useCallback, useMemo} from 'react';
/* eslint-disable react/function-component-definition */
import React, {useCallback, useMemo, useRef} from 'react';

Check failure on line 2 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'useRef' is defined but never used

Check failure on line 2 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'useRef' is defined but never used
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
Expand All @@ -20,11 +21,36 @@
onPress: () => void;
};

function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) {
const Test = () => {

Check failure on line 24 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'Test' is assigned a value but never used

Check failure on line 24 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'Test' is assigned a value but never used
const styles = useThemeStyles();

Check failure on line 25 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'styles' is assigned a value but never used

Check failure on line 25 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'styles' is assigned a value but never used
const theme = useTheme();

Check failure on line 26 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'theme' is assigned a value but never used

Check failure on line 26 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'theme' is assigned a value but never used
const {shouldUseNarrowLayout} = useResponsiveLayout();

Check failure on line 27 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'shouldUseNarrowLayout' is assigned a value but never used

Check failure on line 27 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'shouldUseNarrowLayout' is assigned a value but never used
const {translate, toLocaleOrdinal} = useLocalize();

const approverTitle = useCallback(

Check failure on line 30 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'approverTitle' is assigned a value but never used

Check failure on line 30 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'approverTitle' is assigned a value but never used
(index: number) =>
approvalWorkflow.approvers.length > 1 ? `${toLocaleOrdinal(index + 1, true)} ${translate('workflowsPage.approver').toLowerCase()}` : `${translate('workflowsPage.approver')}`,

Check failure on line 32 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unsafe member access .approvers on an `error` typed value

Check failure on line 32 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unsafe member access .approvers on an `error` typed value

Check failure on line 32 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'approvalWorkflow'.
[approvalWorkflow.approvers.length, toLocaleOrdinal, translate],

Check warning on line 33 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useCallback has an unnecessary dependency: 'approvalWorkflow.approvers.length'. Either exclude it or remove the dependency array. Outer scope values like 'approvalWorkflow.approvers.length' aren't valid dependencies because mutating them doesn't re-render the component

Check failure on line 33 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unsafe member access .approvers on an `error` typed value

Check warning on line 33 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useCallback has an unnecessary dependency: 'approvalWorkflow.approvers.length'. Either exclude it or remove the dependency array. Outer scope values like 'approvalWorkflow.approvers.length' aren't valid dependencies because mutating them doesn't re-render the component

Check failure on line 33 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unsafe member access .approvers on an `error` typed value

Check failure on line 33 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'approvalWorkflow'.
);

const members = useMemo(() => {

Check failure on line 36 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'members' is assigned a value but never used

Check failure on line 36 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

'members' is assigned a value but never used
if (approvalWorkflow.isDefault) {

Check failure on line 37 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Unsafe member access .isDefault on an `error` typed value

Check failure on line 37 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

Unsafe member access .isDefault on an `error` typed value

Check failure on line 37 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'approvalWorkflow'.
return translate('workspace.common.everyone');
}

return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName')

Check failure on line 41 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'approvalWorkflow'.

Check failure on line 41 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Argument of type 'string' is not assignable to parameter of type 'never'.
.map((m) => m.displayName)

Check failure on line 42 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Property 'displayName' does not exist on type 'Partial<Record<never, string | undefined>>'.
.join(', ');
}, [approvalWorkflow.isDefault, translate]);

Check warning on line 44 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useMemo has an unnecessary dependency: 'approvalWorkflow.isDefault'. Either exclude it or remove the dependency array. Outer scope values like 'approvalWorkflow.isDefault' aren't valid dependencies because mutating them doesn't re-render the component

Check warning on line 44 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useMemo has an unnecessary dependency: 'approvalWorkflow.isDefault'. Either exclude it or remove the dependency array. Outer scope values like 'approvalWorkflow.isDefault' aren't valid dependencies because mutating them doesn't re-render the component

Check failure on line 44 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Cannot find name 'approvalWorkflow'.

return <View />;
};

function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {translate, toLocaleOrdinal} = useLocalize();

const approverTitle = useCallback(
(index: number) =>
Expand All @@ -40,7 +66,7 @@
return OptionsListUtils.sortAlphabetically(approvalWorkflow.members, 'displayName')
.map((m) => m.displayName)
.join(', ');
}, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]);
}, [approvalWorkflow]);

Check warning on line 69 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useMemo has a missing dependency: 'translate'. Either include it or remove the dependency array

Check warning on line 69 in src/components/ApprovalWorkflowSection.tsx

View workflow job for this annotation

GitHub Actions / ESLint check

React Hook useMemo has a missing dependency: 'translate'. Either include it or remove the dependency array

return (
<PressableWithoutFeedback
Expand Down Expand Up @@ -110,4 +136,4 @@
);
}

export default ApprovalWorkflowSection;
export {ApprovalWorkflowSection};
4 changes: 1 addition & 3 deletions src/components/AttachmentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ function AttachmentModal({
}

setIsModalOpen(false);
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isModalOpen, isConfirmButtonDisabled, onConfirm, file, sourceState]);

/**
Expand Down Expand Up @@ -458,8 +457,7 @@ function AttachmentModal({
});
}
return menuItems;
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [isReceiptAttachment, transaction, file, sourceState, iouType]);
}, [isReceiptAttachment, canEditReceipt, isOffline, allowDownload, isLocalSource, transaction, translate, closeModal, iouType, report?.reportID, downloadAttachment]);

// There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment.
// props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false.
Expand Down
12 changes: 4 additions & 8 deletions src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import Navigation from '@navigation/Navigation';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import * as IOU from '@userActions/IOU';
import * as Link from '@userActions/Link';
import * as Transaction from '@userActions/Transaction';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
Expand Down Expand Up @@ -84,7 +83,6 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
const session = useSession();
const {isOffline} = useNetwork();
const {translate, toLocaleDigit} = useLocalize();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const parentReportID = report?.parentReportID ?? '-1';
const policyID = report?.policyID ?? '-1';
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}`);
Expand Down Expand Up @@ -188,7 +186,7 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
const shouldShowAttendees = useMemo(() => TransactionUtils.shouldShowAttendees(iouType, policy), [iouType, policy]);

const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest);
const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport);
const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID);
const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction) && !!tripID;

const {getViolationsForField} = useViolations(transactionViolations ?? [], isReceiptBeingScanned || !ReportUtils.isPaidGroupPolicy(report));
Expand Down Expand Up @@ -696,11 +694,9 @@ function MoneyRequestView({report, shouldShowAnimatedBackground, readonly = fals
<MenuItem
title={translate('travel.viewTripDetails')}
icon={Expensicons.Suitcase}
iconRight={Expensicons.NewWindow}
shouldShowRightIcon
onPress={() => {
Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID));
}}
onPress={() =>
Navigation.navigate(ROUTES.TRAVEL_TRIP_DETAILS.getRoute(report?.reportID ?? '-1', transaction?.transactionID ?? '-1', Navigation.getReportRHPActiveRoute()))
}
/>
)}
{shouldShowAttendees && (
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2370,6 +2370,7 @@ const translations = {
hotel: 'Hotel',
car: 'Car',
viewTrip: 'View trip',
tripSupport: 'Trip support',
viewTripDetails: 'View trip details',
trip: 'Trip',
trips: 'Trips',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,7 @@ const translations = {
hotel: 'Hotel',
car: 'Auto',
viewTrip: 'Ver viaje',
tripSupport: 'Soporte de Viaje',
viewTripDetails: 'Ver detalles del viaje',
trip: 'Viaje',
trips: 'Viajes',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator<MoneyRequestNa
const TravelModalStackNavigator = createModalStackNavigator<TravelNavigatorParamList>({
[SCREENS.TRAVEL.MY_TRIPS]: () => require<ReactComponentModule>('../../../../pages/Travel/MyTripsPage').default,
[SCREENS.TRAVEL.TCS]: () => require<ReactComponentModule>('../../../../pages/Travel/TravelTerms').default,
[SCREENS.TRAVEL.TRIP_DETAILS]: () => require<ReactComponentModule>('../../../../pages/Travel/TripDetails').default,
});

const SplitDetailsModalStackNavigator = createModalStackNavigator<SplitDetailsNavigatorParamList>({
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
screens: {
[SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS,
[SCREENS.TRAVEL.TCS]: ROUTES.TRAVEL_TCS,
[SCREENS.TRAVEL.TRIP_DETAILS]: ROUTES.TRAVEL_TRIP_DETAILS.route,
},
},
[SCREENS.RIGHT_MODAL.SEARCH_REPORT]: {
Expand Down
5 changes: 5 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,11 @@ type RightModalNavigatorParamList = {

type TravelNavigatorParamList = {
[SCREENS.TRAVEL.MY_TRIPS]: undefined;
[SCREENS.TRAVEL.TRIP_DETAILS]: {
reportID: string;
transactionID: string;
backTo?: string;
};
};

type FullScreenNavigatorParamList = {
Expand Down
6 changes: 3 additions & 3 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8046,8 +8046,8 @@ function getTripTransactions(tripRoomReportID: string | undefined, reportFieldTo
return tripTransactionReportIDs.flatMap((reportID) => reportsTransactions[reportID ?? ''] ?? []);
}

function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry<Report> | undefined | null): string | undefined {
return getReportOrDraftReport(transactionParentReport?.parentReportID)?.tripData?.tripID;
function getTripIDFromTransactionParentReportID(transactionParentReportID: string | undefined): string | undefined {
return getReportOrDraftReport(transactionParentReportID)?.tripData?.tripID;
}

/**
Expand Down Expand Up @@ -8715,7 +8715,7 @@ export {
updateReportPreview,
temporary_getMoneyRequestOptions,
getTripTransactions,
getTripIDFromTransactionParentReport,
getTripIDFromTransactionParentReportID,
buildOptimisticInvoiceReport,
getInvoiceChatByParticipants,
shouldShowMerchantColumn,
Expand Down
82 changes: 82 additions & 0 deletions src/pages/Travel/TripDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import {NativeModules} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import ScreenWrapper from '@components/ScreenWrapper';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import type {TravelNavigatorParamList} from '@libs/Navigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import * as Link from '@userActions/Link';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';

type TripDetailsProps = StackScreenProps<TravelNavigatorParamList, typeof SCREENS.TRAVEL.TRIP_DETAILS>;

function TripDetails({route}: TripDetailsProps) {
const styles = useThemeStyles();
const theme = useTheme();
const {translate} = useLocalize();
const {canUseSpotnanaTravel} = usePermissions();

const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${route.params.transactionID}`);
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`);
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID ?? '-1'}`);
const tripID = ReportUtils.getTripIDFromTransactionParentReportID(parentReport?.parentReportID);

console.log(`transaction = `, transaction);
console.log(`report = `, report);
console.log(`parentReport = `, parentReport);

return (
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
shouldEnablePickerAvoiding={false}
shouldEnableMaxHeight
testID={TripDetails.displayName}
shouldShowOfflineIndicatorInWideScreen
>
<FullPageNotFoundView
shouldForceFullScreen
shouldShow={!canUseSpotnanaTravel && !NativeModules.HybridAppModule}
>
<HeaderWithBackButton
title={translate('common.details')}
shouldShowBackButton
/>
<MenuItem
title={translate('travel.flightDetails.modifyTrip')}

Check failure on line 56 in src/pages/Travel/TripDetails.tsx

View workflow job for this annotation

GitHub Actions / typecheck

Argument of type '"travel.flightDetails.modifyTrip"' is not assignable to parameter of type 'TranslationPaths'.
icon={Expensicons.Pencil}
iconFill={theme.iconSuccessFill}
iconRight={Expensicons.NewWindow}
shouldShowRightIcon
onPress={() => {
Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID));
}}
/>
<MenuItem
title={translate('travel.tripSupport')}
icon={Expensicons.Phone}
iconFill={theme.iconSuccessFill}
iconRight={Expensicons.NewWindow}
shouldShowRightIcon
onPress={() => {
Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID));
}}
/>
</FullPageNotFoundView>
</ScreenWrapper>
);
}

TripDetails.displayName = 'TripDetails';

export default TripDetails;
Loading