Repository: kiwicom/mobile
Pull request #748 (2018-06-20 10:43:39)

MMB: implement "CarRentalMenuItem" component


Patch:
diff --git a/app/MMB/src/menuGroups/TripInfoMenuGroup.js b/app/MMB/src/menuGroups/TripInfoMenuGroup.js
index d891fc12..39b39134 100644
--- a/app/MMB/src/menuGroups/TripInfoMenuGroup.js
+++ b/app/MMB/src/menuGroups/TripInfoMenuGroup.js
@@ -3,7 +3,11 @@
 import * as React from 'react';
 import { TextIcon } from '@kiwicom/mobile-shared';
 import { Translation } from '@kiwicom/mobile-localization';
-import { MenuItem, TitledMenuGroup } from '@kiwicom/mobile-navigation';
+import {
+  MenuItem,
+  TodoMenuItem,
+  TitledMenuGroup,
+} from '@kiwicom/mobile-navigation';
 
 type Props = {|
   +openSubmenu: (activeId: string, menuId: string) => void,
@@ -32,7 +36,7 @@ export default class TripInfoMenuGroup extends React.Component<Props> {
         icon={<TextIcon code="." />}
         description={<Translation id="mmb.tickets.boarding_passes" />}
       />
-      <MenuItem
+      <TodoMenuItem
         onPress={this.handleOpenExploreSubmenu}
         title={<Translation id="mmb.landing_page.maw.action_button.explore" />}
         isActive={this.props.activeId === 'mmb.main_menu.trip_info.explore'}
diff --git a/app/MMB/src/scenes/tripServices/CarRentalMenuItem.js b/app/MMB/src/scenes/tripServices/CarRentalMenuItem.js
new file mode 100644
index 00000000..850bd795
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/CarRentalMenuItem.js
@@ -0,0 +1,133 @@
+// @flow strict
+
+import * as React from 'react';
+import { Platform } from 'react-native';
+import { graphql, createFragmentContainer } from '@kiwicom/mobile-relay';
+import { TextIcon } from '@kiwicom/mobile-shared';
+import { Translation } from '@kiwicom/mobile-localization';
+import { MenuItem } from '@kiwicom/mobile-navigation';
+import querystring from 'querystring';
+import idx from 'idx';
+
+import LocationsPopup from './LocationsPopup';
+import LocationPopupButton from './LocationPopupButton';
+import type { CarRentalMenuItem as CarRentalMenuItemType } from './__generated__/CarRentalMenuItem.graphql';
+
+type Props = {|
+  +data: CarRentalMenuItemType,
+  +onOpenWebview: string => void,
+|};
+
+type State = {|
+  popupVisible: boolean,
+|};
+
+export class CarRentalMenuItem extends React.Component<Props, State> {
+  state = {
+    popupVisible: false,
+  };
+
+  buildWhitelabelURL = (whitelabelURL: string) => {
+    return (
+      whitelabelURL +
+      (whitelabelURL.includes('?') ? '&' : '?') +
+      querystring.stringify({
+        // hardcoded string for platform identification - mobileapp_android, mobileapp_ios
+        adplat: Platform.select({
+          android: 'mobileapp_android',
+          ios: 'mobileapp_ios',
+        }),
+        forceMobile: true,
+      })
+    );
+  };
+
+  openLink = (whitelabelURL: string) => {
+    this.setState(
+      {
+        popupVisible: false,
+      },
+      () => this.props.onOpenWebview(this.buildWhitelabelURL(whitelabelURL)),
+    );
+  };
+
+  openPopup = () => {
+    const relevantCities =
+      idx(this.props, _ => _.data.carRental.relevantCities) || [];
+
+    if (relevantCities.length === 1) {
+      // do not open the modal for only one whitelabel URL (open it directly)
+      const whitelabelURL = idx(relevantCities, _ => _[0].whitelabelURL);
+      this.openLink(whitelabelURL || '');
+      return;
+    }
+
+    this.setState({
+      popupVisible: true,
+    });
+  };
+
+  hidePopup = () => {
+    this.setState({
+      popupVisible: false,
+    });
+  };
+
+  render = () => {
+    const carRental = idx(this.props, _ => _.data.carRental);
+
+    if (!carRental) {
+      // no car rental service available for this trip (do not render the menu item at all)
+      return null;
+    }
+
+    const relevantCities = idx(carRental, _ => _.relevantCities) || [];
+
+    return (
+      <React.Fragment>
+        <LocationsPopup
+          isVisible={this.state.popupVisible}
+          onClose={this.hidePopup}
+        >
+          {relevantCities.map(relevantCity => {
+            if (!relevantCity) {
+              return null;
+            }
+            const { whitelabelURL, location } = relevantCity;
+            return (
+              <LocationPopupButton
+                key={whitelabelURL}
+                data={location}
+                whitelabelURL={whitelabelURL}
+                onPress={this.openLink}
+              />
+            );
+          })}
+        </LocationsPopup>
+        <MenuItem
+          title={
+            <Translation id="mmb.trip_services.local_services.car_rental" />
+          }
+          onPress={this.openPopup}
+          icon={<TextIcon code="&#xe03a;" />}
+        />
+      </React.Fragment>
+    );
+  };
+}
+
+export default createFragmentContainer(
+  CarRentalMenuItem,
+  graphql`
+    fragment CarRentalMenuItem on WhitelabeledServices {
+      carRental {
+        relevantCities {
+          whitelabelURL
+          location {
+            ...LocationPopupButton
+          }
+        }
+      }
+    }
+  `,
+);
diff --git a/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js b/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
index fc320c38..b3df0925 100644
--- a/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
+++ b/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
@@ -3,15 +3,11 @@
 import * as React from 'react';
 import { TextIcon } from '@kiwicom/mobile-shared';
 import { Translation } from '@kiwicom/mobile-localization';
-import { MenuItem } from '@kiwicom/mobile-navigation';
+import { TodoMenuItem } from '@kiwicom/mobile-navigation';
 
 type Props = {||};
 
 export default class InsuranceMenuItem extends React.Component<Props> {
-  goToInsurance = () => {
-    console.warn('TODO');
-  };
-
   render = () => {
     const insurance = true; // TODO: fetch from the API (at least 48 hours before departure, no US nationality, CONFIRMED status only)
 
@@ -21,11 +17,10 @@ export default class InsuranceMenuItem extends React.Component<Props> {
     }
 
     return (
-      <MenuItem
+      <TodoMenuItem
         title={
           <Translation id="mmb.trip_services.general_services.insurance" />
         }
-        onPress={this.goToInsurance}
         icon={<TextIcon code="'" />}
       />
     );
diff --git a/app/MMB/src/scenes/tripServices/LocationPopupButton.js b/app/MMB/src/scenes/tripServices/LocationPopupButton.js
index c122ff8f..83baeca8 100644
--- a/app/MMB/src/scenes/tripServices/LocationPopupButton.js
+++ b/app/MMB/src/scenes/tripServices/LocationPopupButton.js
@@ -13,9 +13,14 @@ type Props = {|
   +whitelabelURL: string,
   +data: LocationPopupButtonType,
   +onPress: string => void,
+  +displayIata?: boolean,
 |};
 
 class LocationPopupButton extends React.Component<Props> {
+  static defaultProps = {
+    displayIata: false,
+  };
+
   openWhitelabel = () => {
     this.props.onPress(this.props.whitelabelURL);
   };
@@ -28,9 +33,13 @@ class LocationPopupButton extends React.Component<Props> {
       <Touchable onPress={this.openWhitelabel} style={styleSheet.wrapper}>
         <React.Fragment>
           <CountryFlag data={this.props.data} />
-          <Translation
-            passThrough={`in ${cityName || ''} (${locationId || ''})`}
-          />
+          {this.props.displayIata ? (
+            <Translation
+              passThrough={`in ${cityName || ''} (${locationId || ''})`}
+            />
+          ) : (
+            <Translation passThrough={cityName || ''} />
+          )}
         </React.Fragment>
       </Touchable>
     );
diff --git a/app/MMB/src/scenes/tripServices/LocationsPopup.js b/app/MMB/src/scenes/tripServices/LocationsPopup.js
new file mode 100644
index 00000000..77e71812
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/LocationsPopup.js
@@ -0,0 +1,43 @@
+// @flow strict
+
+import * as React from 'react';
+import { View } from 'react-native';
+import { Popup, StyleSheet } from '@kiwicom/mobile-shared';
+import { Translation } from '@kiwicom/mobile-localization';
+import {
+  MenuGroup,
+  MenuGroupTitle,
+  SeparatorTrimmed,
+} from '@kiwicom/mobile-navigation';
+
+type Props = {|
+  +isVisible: boolean,
+  +onClose: () => void,
+  +children: React.Node,
+|};
+
+export default function LocationsPopup(props: Props) {
+  return (
+    <Popup isVisible={props.isVisible} onClose={props.onClose}>
+      <View style={styleSheet.popupView}>
+        <MenuGroupTitle
+          title={<Translation id="mmb.trip_services.locations_popup.title" />}
+        />
+
+        <MenuGroup
+          customSeparator={
+            <SeparatorTrimmed gapSizeStart={15} gapSizeEnd={15} />
+          }
+        >
+          {props.children}
+        </MenuGroup>
+      </View>
+    </Popup>
+  );
+}
+
+const styleSheet = StyleSheet.create({
+  popupView: {
+    padding: 5,
+  },
+});
diff --git a/app/MMB/src/scenes/tripServices/LoungeMenuItem.js b/app/MMB/src/scenes/tripServices/LoungeMenuItem.js
index c23e0a52..70ba0bdc 100644
--- a/app/MMB/src/scenes/tripServices/LoungeMenuItem.js
+++ b/app/MMB/src/scenes/tripServices/LoungeMenuItem.js
@@ -1,19 +1,15 @@
 // @flow strict
 
 import * as React from 'react';
-import { Platform, View } from 'react-native';
+import { Platform } from 'react-native';
 import { graphql, createFragmentContainer } from '@kiwicom/mobile-relay';
-import { Popup, TextIcon, StyleSheet } from '@kiwicom/mobile-shared';
+import { TextIcon } from '@kiwicom/mobile-shared';
 import { Translation } from '@kiwicom/mobile-localization';
-import {
-  MenuItem,
-  MenuGroup,
-  MenuGroupTitle,
-  SeparatorTrimmed,
-} from '@kiwicom/mobile-navigation';
+import { MenuItem } from '@kiwicom/mobile-navigation';
 import querystring from 'querystring';
 import idx from 'idx';
 
+import LocationsPopup from './LocationsPopup';
 import LocationPopupButton from './LocationPopupButton';
 import type { LoungeMenuItem as LoungeMenuItemType } from './__generated__/LoungeMenuItem.graphql';
 
@@ -87,34 +83,26 @@ class LoungeMenuItem extends React.Component<Props, State> {
 
     return (
       <React.Fragment>
-        <Popup isVisible={this.state.popupVisible} onClose={this.hidePopup}>
-          <View style={styleSheet.popupView}>
-            <MenuGroupTitle
-              title={<Translation id="mmb.trip_services.lounge.popup.title" />}
-            />
-
-            <MenuGroup
-              customSeparator={
-                <SeparatorTrimmed gapSizeStart={15} gapSizeEnd={15} />
-              }
-            >
-              {relevantAirports.map(relevantAirport => {
-                if (!relevantAirport) {
-                  return null;
-                }
-                const { whitelabelURL, location } = relevantAirport;
-                return (
-                  <LocationPopupButton
-                    key={whitelabelURL}
-                    data={location}
-                    whitelabelURL={whitelabelURL}
-                    onPress={this.openLoungeLink}
-                  />
-                );
-              })}
-            </MenuGroup>
-          </View>
-        </Popup>
+        <LocationsPopup
+          isVisible={this.state.popupVisible}
+          onClose={this.hidePopup}
+        >
+          {relevantAirports.map(relevantAirport => {
+            if (!relevantAirport) {
+              return null;
+            }
+            const { whitelabelURL, location } = relevantAirport;
+            return (
+              <LocationPopupButton
+                key={whitelabelURL}
+                data={location}
+                whitelabelURL={whitelabelURL}
+                onPress={this.openLoungeLink}
+                displayIata={true}
+              />
+            );
+          })}
+        </LocationsPopup>
 
         <MenuItem
           title={<Translation id="mmb.trip_services.local_services.lounge" />}
@@ -129,9 +117,8 @@ class LoungeMenuItem extends React.Component<Props, State> {
 export default createFragmentContainer(
   LoungeMenuItem,
   graphql`
-    fragment LoungeMenuItem on WhitelabeledServices
-      @argumentDefinitions(departureTime: { type: "DateTime!" }) {
-      lounge(departureTime: $departureTime) {
+    fragment LoungeMenuItem on WhitelabeledServices {
+      lounge {
         relevantAirports {
           whitelabelURL
           location {
@@ -142,9 +129,3 @@ export default createFragmentContainer(
     }
   `,
 );
-
-const styleSheet = StyleSheet.create({
-  popupView: {
-    padding: 5,
-  },
-});
diff --git a/app/MMB/src/scenes/tripServices/ParkingMenuItem.js b/app/MMB/src/scenes/tripServices/ParkingMenuItem.js
index 5c7e523e..52c821bd 100644
--- a/app/MMB/src/scenes/tripServices/ParkingMenuItem.js
+++ b/app/MMB/src/scenes/tripServices/ParkingMenuItem.js
@@ -56,12 +56,8 @@ class ParkingMenuItem extends React.Component<Props> {
 export default createFragmentContainer(
   ParkingMenuItem,
   graphql`
-    fragment ParkingMenuItem on WhitelabeledServices
-      @argumentDefinitions(
-        departureTime: { type: "DateTime!" }
-        arrivalTime: { type: "DateTime!" }
-      ) {
-      parking(fromDate: $departureTime, toDate: $arrivalTime) {
+    fragment ParkingMenuItem on WhitelabeledServices {
+      parking {
         whitelabelURL
       }
     }
diff --git a/app/MMB/src/scenes/tripServices/TripServices.js b/app/MMB/src/scenes/tripServices/TripServices.js
index 8f8b09c6..550d566f 100644
--- a/app/MMB/src/scenes/tripServices/TripServices.js
+++ b/app/MMB/src/scenes/tripServices/TripServices.js
@@ -7,7 +7,7 @@ import { TextIcon, WebView } from '@kiwicom/mobile-shared';
 import { PrivateApiRenderer, graphql } from '@kiwicom/mobile-relay';
 import {
   TitledMenuGroup,
-  MenuItem,
+  TodoMenuItem,
   type RouteNamesType,
   type NavigationType,
 } from '@kiwicom/mobile-navigation';
@@ -16,13 +16,10 @@ import idx from 'idx';
 import LoungeMenuItem from './LoungeMenuItem';
 import ParkingMenuItem from './ParkingMenuItem';
 import InsuranceMenuItem from './InsuranceMenuItem';
+import CarRentalMenuItem from './CarRentalMenuItem';
 import BookingDetailContext from '../../context/BookingDetailContext';
 import type { TripServicesQueryResponse } from './__generated__/TripServicesQuery.graphql';
 
-const VoidAction = () => {
-  console.warn('TODO');
-};
-
 type Props = {|
   +navigation: NavigationType,
 |};
@@ -61,16 +58,12 @@ export default class TripServices extends React.Component<Props> {
         <TitledMenuGroup
           title={<Translation id="mmb.trip_services.local_services" />}
         >
-          <MenuItem
-            title={
-              <Translation id="mmb.trip_services.local_services.car_rental" />
-            }
-            onPress={VoidAction}
-            icon={<TextIcon code="&#xe03a;" />}
+          <CarRentalMenuItem
+            data={availableWhitelabeledServices}
+            onOpenWebview={this.openWebview}
           />
-          <MenuItem
+          <TodoMenuItem
             title={<Translation id="mmb.trip_services.local_services.hotel" />}
-            onPress={VoidAction}
             icon={<TextIcon code="&#xe029;" />}
           />
           <LoungeMenuItem
@@ -81,11 +74,10 @@ export default class TripServices extends React.Component<Props> {
             data={availableWhitelabeledServices}
             onOpenWebview={this.openWebview}
           />
-          <MenuItem
+          <TodoMenuItem
             title={
               <Translation id="mmb.trip_services.local_services.transportation" />
             }
-            onPress={VoidAction}
             icon={<TextIcon code="<" />}
           />
         </TitledMenuGroup>
@@ -95,23 +87,16 @@ export default class TripServices extends React.Component<Props> {
 
   render = () => (
     <BookingDetailContext.Consumer>
-      {({ bookingId, departureTime, arrivalTime }) => (
+      {({ bookingId }) => (
         <PrivateApiRenderer
           query={graphql`
-            query TripServicesQuery(
-              $bookingId: ID!
-              $departureTime: DateTime!
-              $arrivalTime: DateTime!
-            ) {
+            query TripServicesQuery($bookingId: ID!) {
               node(id: $bookingId) {
                 ... on BookingInterface {
                   availableWhitelabeledServices {
-                    ...LoungeMenuItem @arguments(departureTime: $departureTime)
+                    ...CarRentalMenuItem
+                    ...LoungeMenuItem
                     ...ParkingMenuItem
-                      @arguments(
-                        departureTime: $departureTime
-                        arrivalTime: $arrivalTime
-                      )
                   }
                 }
               }
@@ -119,8 +104,6 @@ export default class TripServices extends React.Component<Props> {
           `}
           variables={{
             bookingId,
-            departureTime,
-            arrivalTime,
           }}
           render={this.renderLocalServices}
         />
diff --git a/app/MMB/src/scenes/tripServices/__generated__/CarRentalMenuItem.graphql.js b/app/MMB/src/scenes/tripServices/__generated__/CarRentalMenuItem.graphql.js
new file mode 100644
index 00000000..2f3c5dd5
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/__generated__/CarRentalMenuItem.graphql.js
@@ -0,0 +1,84 @@
+/**
+ * @flow
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ConcreteFragment } from 'relay-runtime';
+type LocationPopupButton$ref = any;
+import type { FragmentReference } from "relay-runtime";
+declare export opaque type CarRentalMenuItem$ref: FragmentReference;
+export type CarRentalMenuItem = {|
+  +carRental: ?{|
+    +relevantCities: ?$ReadOnlyArray<?{|
+      +whitelabelURL: ?string,
+      +location: ?{|
+        +$fragmentRefs: LocationPopupButton$ref
+      |},
+    |}>
+  |},
+  +$refType: CarRentalMenuItem$ref,
+|};
+*/
+
+
+const node/*: ConcreteFragment*/ = {
+  "kind": "Fragment",
+  "name": "CarRentalMenuItem",
+  "type": "WhitelabeledServices",
+  "metadata": null,
+  "argumentDefinitions": [],
+  "selections": [
+    {
+      "kind": "LinkedField",
+      "alias": null,
+      "name": "carRental",
+      "storageKey": null,
+      "args": null,
+      "concreteType": "CarRentalService",
+      "plural": false,
+      "selections": [
+        {
+          "kind": "LinkedField",
+          "alias": null,
+          "name": "relevantCities",
+          "storageKey": null,
+          "args": null,
+          "concreteType": "CarRentalServiceRelevantCities",
+          "plural": true,
+          "selections": [
+            {
+              "kind": "ScalarField",
+              "alias": null,
+              "name": "whitelabelURL",
+              "args": null,
+              "storageKey": null
+            },
+            {
+              "kind": "LinkedField",
+              "alias": null,
+              "name": "location",
+              "storageKey": null,
+              "args": null,
+              "concreteType": "Location",
+              "plural": false,
+              "selections": [
+                {
+                  "kind": "FragmentSpread",
+                  "name": "LocationPopupButton",
+                  "args": null
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+};
+// prettier-ignore
+(node/*: any*/).hash = 'a241be08faf7914eca795bd6780839a4';
+module.exports = node;
diff --git a/app/MMB/src/scenes/tripServices/__generated__/LoungeMenuItem.graphql.js b/app/MMB/src/scenes/tripServices/__generated__/LoungeMenuItem.graphql.js
index 6aeedb01..678f5fad 100644
--- a/app/MMB/src/scenes/tripServices/__generated__/LoungeMenuItem.graphql.js
+++ b/app/MMB/src/scenes/tripServices/__generated__/LoungeMenuItem.graphql.js
@@ -30,28 +30,14 @@ const node/*: ConcreteFragment*/ = {
   "name": "LoungeMenuItem",
   "type": "WhitelabeledServices",
   "metadata": null,
-  "argumentDefinitions": [
-    {
-      "kind": "LocalArgument",
-      "name": "departureTime",
-      "type": "DateTime!",
-      "defaultValue": null
-    }
-  ],
+  "argumentDefinitions": [],
   "selections": [
     {
       "kind": "LinkedField",
       "alias": null,
       "name": "lounge",
       "storageKey": null,
-      "args": [
-        {
-          "kind": "Variable",
-          "name": "departureTime",
-          "variableName": "departureTime",
-          "type": "DateTime!"
-        }
-      ],
+      "args": null,
       "concreteType": "LoungeService",
       "plural": false,
       "selections": [
@@ -94,5 +80,5 @@ const node/*: ConcreteFragment*/ = {
   ]
 };
 // prettier-ignore
-(node/*: any*/).hash = '8d5bd34e51fff1d73a7a42959648b178';
+(node/*: any*/).hash = '681e3b6818a52aa4d89fee568b80a528';
 module.exports = node;
diff --git a/app/MMB/src/scenes/tripServices/__generated__/ParkingMenuItem.graphql.js b/app/MMB/src/scenes/tripServices/__generated__/ParkingMenuItem.graphql.js
index ad43dd96..44b1d7a1 100644
--- a/app/MMB/src/scenes/tripServices/__generated__/ParkingMenuItem.graphql.js
+++ b/app/MMB/src/scenes/tripServices/__generated__/ParkingMenuItem.graphql.js
@@ -24,40 +24,14 @@ const node/*: ConcreteFragment*/ = {
   "name": "ParkingMenuItem",
   "type": "WhitelabeledServices",
   "metadata": null,
-  "argumentDefinitions": [
-    {
-      "kind": "LocalArgument",
-      "name": "departureTime",
-      "type": "DateTime!",
-      "defaultValue": null
-    },
-    {
-      "kind": "LocalArgument",
-      "name": "arrivalTime",
-      "type": "DateTime!",
-      "defaultValue": null
-    }
-  ],
+  "argumentDefinitions": [],
   "selections": [
     {
       "kind": "LinkedField",
       "alias": null,
       "name": "parking",
       "storageKey": null,
-      "args": [
-        {
-          "kind": "Variable",
-          "name": "fromDate",
-          "variableName": "departureTime",
-          "type": "DateTime!"
-        },
-        {
-          "kind": "Variable",
-          "name": "toDate",
-          "variableName": "arrivalTime",
-          "type": "DateTime!"
-        }
-      ],
+      "args": null,
       "concreteType": "ParkingService",
       "plural": false,
       "selections": [
@@ -73,5 +47,5 @@ const node/*: ConcreteFragment*/ = {
   ]
 };
 // prettier-ignore
-(node/*: any*/).hash = 'b97fd398cd900a682de7cfe712264018';
+(node/*: any*/).hash = '82e9bb44eeb965f6893bcad381cc19b5';
 module.exports = node;
diff --git a/app/MMB/src/scenes/tripServices/__generated__/TripServicesQuery.graphql.js b/app/MMB/src/scenes/tripServices/__generated__/TripServicesQuery.graphql.js
index 46b17351..23e9369b 100644
--- a/app/MMB/src/scenes/tripServices/__generated__/TripServicesQuery.graphql.js
+++ b/app/MMB/src/scenes/tripServices/__generated__/TripServicesQuery.graphql.js
@@ -1,6 +1,6 @@
 /**
  * @flow
- * @relayHash 1e86acbbdadf745c3794a9eeec9b989a
+ * @relayHash 5032eb09d5ce4bf2fc26c463107fa8a6
  */
 
 /* eslint-disable */
@@ -9,17 +9,16 @@
 
 /*::
 import type { ConcreteRequest } from 'relay-runtime';
+type CarRentalMenuItem$ref = any;
 type LoungeMenuItem$ref = any;
 type ParkingMenuItem$ref = any;
 export type TripServicesQueryVariables = {|
-  bookingId: string,
-  departureTime: any,
-  arrivalTime: any,
+  bookingId: string
 |};
 export type TripServicesQueryResponse = {|
   +node: ?{|
     +availableWhitelabeledServices?: ?{|
-      +$fragmentRefs: LoungeMenuItem$ref & ParkingMenuItem$ref
+      +$fragmentRefs: CarRentalMenuItem$ref & LoungeMenuItem$ref & ParkingMenuItem$ref
     |}
   |}
 |};
@@ -29,23 +28,34 @@ export type TripServicesQueryResponse = {|
 /*
 query TripServicesQuery(
   $bookingId: ID!
-  $departureTime: DateTime!
-  $arrivalTime: DateTime!
 ) {
   node(id: $bookingId) {
     __typename
     ... on BookingInterface {
       availableWhitelabeledServices {
-        ...LoungeMenuItem_WJqJd
-        ...ParkingMenuItem_3cQ5np
+        ...CarRentalMenuItem
+        ...LoungeMenuItem
+        ...ParkingMenuItem
       }
     }
     id
   }
 }
 
-fragment LoungeMenuItem_WJqJd on WhitelabeledServices {
-  lounge(departureTime: $departureTime) {
+fragment CarRentalMenuItem on WhitelabeledServices {
+  carRental {
+    relevantCities {
+      whitelabelURL
+      location {
+        ...LocationPopupButton
+        id
+      }
+    }
+  }
+}
+
+fragment LoungeMenuItem on WhitelabeledServices {
+  lounge {
     relevantAirports {
       whitelabelURL
       location {
@@ -56,8 +66,8 @@ fragment LoungeMenuItem_WJqJd on WhitelabeledServices {
   }
 }
 
-fragment ParkingMenuItem_3cQ5np on WhitelabeledServices {
-  parking(fromDate: $departureTime, toDate: $arrivalTime) {
+fragment ParkingMenuItem on WhitelabeledServices {
+  parking {
     whitelabelURL
   }
 }
@@ -82,18 +92,6 @@ var v0 = [
     "name": "bookingId",
     "type": "ID!",
     "defaultValue": null
-  },
-  {
-    "kind": "LocalArgument",
-    "name": "departureTime",
-    "type": "DateTime!",
-    "defaultValue": null
-  },
-  {
-    "kind": "LocalArgument",
-    "name": "arrivalTime",
-    "type": "DateTime!",
-    "defaultValue": null
   }
 ],
 v1 = [
@@ -105,31 +103,72 @@ v1 = [
   }
 ],
 v2 = {
-  "kind": "Variable",
-  "name": "departureTime",
-  "variableName": "departureTime",
-  "type": null
-},
-v3 = {
   "kind": "ScalarField",
   "alias": null,
   "name": "whitelabelURL",
   "args": null,
   "storageKey": null
 },
-v4 = {
+v3 = {
   "kind": "ScalarField",
   "alias": null,
   "name": "id",
   "args": null,
   "storageKey": null
-};
+},
+v4 = [
+  v2,
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "location",
+    "storageKey": null,
+    "args": null,
+    "concreteType": "Location",
+    "plural": false,
+    "selections": [
+      {
+        "kind": "LinkedField",
+        "alias": null,
+        "name": "city",
+        "storageKey": null,
+        "args": null,
+        "concreteType": "LocationArea",
+        "plural": false,
+        "selections": [
+          {
+            "kind": "ScalarField",
+            "alias": null,
+            "name": "name",
+            "args": null,
+            "storageKey": null
+          }
+        ]
+      },
+      {
+        "kind": "ScalarField",
+        "alias": null,
+        "name": "locationId",
+        "args": null,
+        "storageKey": null
+      },
+      {
+        "kind": "ScalarField",
+        "alias": null,
+        "name": "countryFlagURL",
+        "args": null,
+        "storageKey": null
+      },
+      v3
+    ]
+  }
+];
 return {
   "kind": "Request",
   "operationKind": "query",
   "name": "TripServicesQuery",
   "id": null,
-  "text": "query TripServicesQuery(\n  $bookingId: ID!\n  $departureTime: DateTime!\n  $arrivalTime: DateTime!\n) {\n  node(id: $bookingId) {\n    __typename\n    ... on BookingInterface {\n      availableWhitelabeledServices {\n        ...LoungeMenuItem_WJqJd\n        ...ParkingMenuItem_3cQ5np\n      }\n    }\n    id\n  }\n}\n\nfragment LoungeMenuItem_WJqJd on WhitelabeledServices {\n  lounge(departureTime: $departureTime) {\n    relevantAirports {\n      whitelabelURL\n      location {\n        ...LocationPopupButton\n        id\n      }\n    }\n  }\n}\n\nfragment ParkingMenuItem_3cQ5np on WhitelabeledServices {\n  parking(fromDate: $departureTime, toDate: $arrivalTime) {\n    whitelabelURL\n  }\n}\n\nfragment LocationPopupButton on Location {\n  city {\n    name\n  }\n  locationId\n  ...CountryFlag\n}\n\nfragment CountryFlag on Location {\n  countryFlagURL\n}\n",
+  "text": "query TripServicesQuery(\n  $bookingId: ID!\n) {\n  node(id: $bookingId) {\n    __typename\n    ... on BookingInterface {\n      availableWhitelabeledServices {\n        ...CarRentalMenuItem\n        ...LoungeMenuItem\n        ...ParkingMenuItem\n      }\n    }\n    id\n  }\n}\n\nfragment CarRentalMenuItem on WhitelabeledServices {\n  carRental {\n    relevantCities {\n      whitelabelURL\n      location {\n        ...LocationPopupButton\n        id\n      }\n    }\n  }\n}\n\nfragment LoungeMenuItem on WhitelabeledServices {\n  lounge {\n    relevantAirports {\n      whitelabelURL\n      location {\n        ...LocationPopupButton\n        id\n      }\n    }\n  }\n}\n\nfragment ParkingMenuItem on WhitelabeledServices {\n  parking {\n    whitelabelURL\n  }\n}\n\nfragment LocationPopupButton on Location {\n  city {\n    name\n  }\n  locationId\n  ...CountryFlag\n}\n\nfragment CountryFlag on Location {\n  countryFlagURL\n}\n",
   "metadata": {},
   "fragment": {
     "kind": "Fragment",
@@ -156,25 +195,20 @@ return {
             "concreteType": "WhitelabeledServices",
             "plural": false,
             "selections": [
+              {
+                "kind": "FragmentSpread",
+                "name": "CarRentalMenuItem",
+                "args": null
+              },
               {
                 "kind": "FragmentSpread",
                 "name": "LoungeMenuItem",
-                "args": [
-                  v2
-                ]
+                "args": null
               },
               {
                 "kind": "FragmentSpread",
                 "name": "ParkingMenuItem",
-                "args": [
-                  {
-                    "kind": "Variable",
-                    "name": "arrivalTime",
-                    "variableName": "arrivalTime",
-                    "type": null
-                  },
-                  v2
-                ]
+                "args": null
               }
             ]
           }
@@ -215,16 +249,30 @@ return {
               {
                 "kind": "LinkedField",
                 "alias": null,
-                "name": "lounge",
+                "name": "carRental",
                 "storageKey": null,
-                "args": [
+                "args": null,
+                "concreteType": "CarRentalService",
+                "plural": false,
+                "selections": [
                   {
-                    "kind": "Variable",
-                    "name": "departureTime",
-                    "variableName": "departureTime",
-                    "type": "DateTime!"
+                    "kind": "LinkedField",
+                    "alias": null,
+                    "name": "relevantCities",
+                    "storageKey": null,
+                    "args": null,
+                    "concreteType": "CarRentalServiceRelevantCities",
+                    "plural": true,
+                    "selections": v4
                   }
-                ],
+                ]
+              },
+              {
+                "kind": "LinkedField",
+                "alias": null,
+                "name": "lounge",
+                "storageKey": null,
+                "args": null,
                 "concreteType": "LoungeService",
                 "plural": false,
                 "selections": [
@@ -236,53 +284,7 @@ return {
                     "args": null,
                     "concreteType": "LoungeServiceRelevantAirports",
                     "plural": true,
-                    "selections": [
-                      v3,
-                      {
-                        "kind": "LinkedField",
-                        "alias": null,
-                        "name": "location",
-                        "storageKey": null,
-                        "args": null,
-                        "concreteType": "Location",
-                        "plural": false,
-                        "selections": [
-                          {
-                            "kind": "LinkedField",
-                            "alias": null,
-                            "name": "city",
-                            "storageKey": null,
-                            "args": null,
-                            "concreteType": "LocationArea",
-                            "plural": false,
-                            "selections": [
-                              {
-                                "kind": "ScalarField",
-                                "alias": null,
-                                "name": "name",
-                                "args": null,
-                                "storageKey": null
-                              }
-                            ]
-                          },
-                          {
-                            "kind": "ScalarField",
-                            "alias": null,
-                            "name": "locationId",
-                            "args": null,
-                            "storageKey": null
-                          },
-                          {
-                            "kind": "ScalarField",
-                            "alias": null,
-                            "name": "countryFlagURL",
-                            "args": null,
-                            "storageKey": null
-                          },
-                          v4
-                        ]
-                      }
-                    ]
+                    "selections": v4
                   }
                 ]
               },
@@ -291,29 +293,16 @@ return {
                 "alias": null,
                 "name": "parking",
                 "storageKey": null,
-                "args": [
-                  {
-                    "kind": "Variable",
-                    "name": "fromDate",
-                    "variableName": "departureTime",
-                    "type": "DateTime!"
-                  },
-                  {
-                    "kind": "Variable",
-                    "name": "toDate",
-                    "variableName": "arrivalTime",
-                    "type": "DateTime!"
-                  }
-                ],
+                "args": null,
                 "concreteType": "ParkingService",
                 "plural": false,
                 "selections": [
-                  v3
+                  v2
                 ]
               }
             ]
           },
-          v4
+          v3
         ]
       }
     ]
@@ -321,5 +310,5 @@ return {
 };
 })();
 // prettier-ignore
-(node/*: any*/).hash = '6b2a1de3d5dc0f333c8facc7c4b92c11';
+(node/*: any*/).hash = '769723bb0882609f095aba72b3ab38b0';
 module.exports = node;
diff --git a/packages/localization/src/vocabularies/ManageMyBooking.js b/packages/localization/src/vocabularies/ManageMyBooking.js
index 19e5ae97..50ed5a6f 100644
--- a/packages/localization/src/vocabularies/ManageMyBooking.js
+++ b/packages/localization/src/vocabularies/ManageMyBooking.js
@@ -74,7 +74,7 @@ export default {
   'mmb.trip_services.local_services.lounge': 'Lounge',
   'mmb.trip_services.local_services.parking': 'Parking',
   'mmb.trip_services.local_services.transportation': 'Transportation',
-  'mmb.trip_services.lounge.popup.title': 'For which part of the trip?',
+  'mmb.trip_services.locations_popup.title': 'For which part of the trip?',
 
   // MMB Support
   'mmb.support.phone.suggested': 'Suggested',
diff --git a/packages/navigation/index.js b/packages/navigation/index.js
index d63a89ed..4a6ccad4 100644
--- a/packages/navigation/index.js
+++ b/packages/navigation/index.js
@@ -16,6 +16,7 @@ import type { TranslationType } from '@kiwicom/mobile-localization';
 export { default as MenuGroup } from './src/MenuGroup';
 export { default as MenuGroupTitle } from './src/MenuGroupTitle';
 export { default as MenuItem } from './src/MenuItem';
+export { default as TodoMenuItem } from './src/TodoMenuItem';
 export { SeparatorTrimmed, SeparatorFullWidth } from './src/Separators';
 export { default as TitledMenuGroup } from './src/TitledMenuGroup';
 
diff --git a/packages/navigation/src/TodoMenuItem.js b/packages/navigation/src/TodoMenuItem.js
new file mode 100644
index 00000000..a9d5ae08
--- /dev/null
+++ b/packages/navigation/src/TodoMenuItem.js
@@ -0,0 +1,46 @@
+// @flow
+
+import * as React from 'react';
+import { Text, StyleSheet } from '@kiwicom/mobile-shared';
+
+import MenuItem from './MenuItem';
+
+const VoidAction = () => {
+  console.warn('TODO - this menu item is not ready yet');
+};
+
+// this type should be compatible with the type from "MenuItem" component
+type Props = {|
+  title: React.Element<*>,
+  onPress?: () => void,
+  isActive?: boolean,
+  icon?: React.Element<*>,
+  description?: React.Element<*>,
+|};
+
+export default function TodoMenuItem(props: Props) {
+  return (
+    <MenuItem
+      onPress={VoidAction}
+      isActive={false}
+      {...props}
+      title={
+        // $FlowExpectedError: should be translation but works with any component
+        <React.Fragment>
+          <Text style={styleSheet.text}>{props.title}</Text>{' '}
+          {/* $FlowExpectedError: should be translation but works with any component */}
+          <Text style={styleSheet.textTodo}>(TODO)</Text>
+        </React.Fragment>
+      }
+    />
+  );
+}
+
+const styleSheet = StyleSheet.create({
+  text: {
+    textDecorationLine: 'line-through',
+  },
+  textTodo: {
+    color: 'red',
+  },
+});
diff --git a/schema.graphql b/schema.graphql
index f698f15b..241747b3 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -460,6 +460,15 @@ enum BookingType {
   MULTICITY
 }
 
+type CarRentalService {
+  relevantCities: [CarRentalServiceRelevantCities]
+}
+
+type CarRentalServiceRelevantCities {
+  whitelabelURL: String
+  location: Location
+}
+
 type Carrier {
   """The ID of an object"""
   id: ID!
@@ -1846,7 +1855,9 @@ type RootQuery {
 
   """Find booking by its id or databaseId."""
   booking(
-    """Both Booking id & databaseId can be used as ID."""
+    """
+    Database ID (human readable ID) of the booking. You should use "node" query if you want to use opaque ID.
+    """
     id: ID!
   ): Booking @deprecated(reason: "Use \"node\" query instead.")
 
@@ -2022,6 +2033,7 @@ type Visa {
 }
 
 type WhitelabeledServices {
-  lounge(departureTime: DateTime!): LoungeService
-  parking(fromDate: DateTime!, toDate: DateTime!): ParkingService
+  lounge: LoungeService
+  parking: ParkingService
+  carRental: CarRentalService
 }


Test patch:
diff --git a/app/MMB/src/scenes/tripServices/__tests__/CarRentalMenuItem.openLink.test.js b/app/MMB/src/scenes/tripServices/__tests__/CarRentalMenuItem.openLink.test.js
new file mode 100644
index 00000000..70416613
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/__tests__/CarRentalMenuItem.openLink.test.js
@@ -0,0 +1,17 @@
+// @flow strict
+
+import { CarRentalMenuItem } from '../CarRentalMenuItem';
+
+it('adds correct parameters to the URL', () => {
+  const Component = new CarRentalMenuItem();
+  expect(Component.buildWhitelabelURL('MOCKED')).toBe(
+    'MOCKED?adplat=mobileapp_ios&forceMobile=true',
+  );
+});
+
+it('appends parameters correctly', () => {
+  const Component = new CarRentalMenuItem();
+  expect(Component.buildWhitelabelURL('MOCKED?test=true')).toBe(
+    'MOCKED?test=true&adplat=mobileapp_ios&forceMobile=true',
+  );
+});