Repository: kiwicom/mobile
Pull request #750 (2018-06-21 09:32:26)

MMB: prepare insurance overview screen


Patch:
diff --git a/app/MMB/src/components/header/Header.js b/app/MMB/src/components/header/Header.js
index 4633e0e7..58342b10 100644
--- a/app/MMB/src/components/header/Header.js
+++ b/app/MMB/src/components/header/Header.js
@@ -1,15 +1,8 @@
 // @flow
 
 import * as React from 'react';
-import { View } from 'react-native';
 import { graphql, createFragmentContainer } from '@kiwicom/mobile-relay';
-import {
-  StyleSheet,
-  Color,
-  AdaptableLayout,
-  Touchable,
-  TextIcon,
-} from '@kiwicom/mobile-shared';
+import { AdaptableLayout, Touchable, TextIcon } from '@kiwicom/mobile-shared';
 import { SeparatorFullWidth, MenuItem } from '@kiwicom/mobile-navigation';
 import { Translation } from '@kiwicom/mobile-localization';
 import idx from 'idx';
@@ -29,13 +22,9 @@ function HeaderShared(props: HeaderSharedProps) {
 
   return (
     <React.Fragment>
-      <View style={styleSheet.wrapper}>
-        <StatusBar data={booking} />
-        <View style={styleSheet.separator}>
-          <SeparatorFullWidth />
-        </View>
-        {props.children}
-      </View>
+      <StatusBar data={booking} />
+      <SeparatorFullWidth />
+      {props.children}
     </React.Fragment>
   );
 }
@@ -116,13 +105,3 @@ export default createFragmentContainer(
     }
   `,
 );
-
-const styleSheet = StyleSheet.create({
-  wrapper: {
-    backgroundColor: Color.white,
-    padding: 10,
-  },
-  separator: {
-    marginVertical: 10,
-  },
-});
diff --git a/app/MMB/src/components/header/StatusBar.js b/app/MMB/src/components/header/StatusBar.js
index cce19917..7004d871 100644
--- a/app/MMB/src/components/header/StatusBar.js
+++ b/app/MMB/src/components/header/StatusBar.js
@@ -11,7 +11,7 @@ import StatusBarIcon from './StatusBarIcon';
 import type { StatusBar as StatusBarType } from './__generated__/StatusBar.graphql';
 
 type Props = {|
-  data: StatusBarType,
+  +data: StatusBarType,
 |};
 
 function StatusBar(props: Props) {
@@ -42,6 +42,8 @@ export default createFragmentContainer(
 const styleSheet = StyleSheet.create({
   row: {
     flexDirection: 'row',
+    backgroundColor: Color.white,
+    padding: 10,
   },
   icon: {
     flexGrow: 1,
diff --git a/app/MMB/src/components/header/TripInfo.js b/app/MMB/src/components/header/TripInfo.js
index 9b319f6d..ef00ac9d 100644
--- a/app/MMB/src/components/header/TripInfo.js
+++ b/app/MMB/src/components/header/TripInfo.js
@@ -1,6 +1,8 @@
 // @flow
 
 import * as React from 'react';
+import { View } from 'react-native';
+import { StyleSheet, Color } from '@kiwicom/mobile-shared';
 import { graphql, createFragmentContainer } from '@kiwicom/mobile-relay';
 import idx from 'idx';
 
@@ -10,7 +12,7 @@ import TripInfoMulticity from './TripInfoMulticity';
 import type { TripInfo as TripInfoType } from './__generated__/TripInfo.graphql';
 
 type Props = {|
-  data: TripInfoType,
+  +data: TripInfoType,
 |};
 
 function TripInfo(props: Props) {
@@ -18,11 +20,11 @@ function TripInfo(props: Props) {
   const type = idx(data, _ => _.__typename);
 
   return (
-    <React.Fragment>
+    <View style={styleSheet.wrapper}>
       {type === 'BookingOneWay' && <TripInfoOneWay data={data} />}
       {type === 'BookingReturn' && <TripInfoReturn data={data} />}
       {type === 'BookingMulticity' && <TripInfoMulticity data={data} />}
-    </React.Fragment>
+    </View>
   );
 }
 
@@ -43,3 +45,10 @@ export default createFragmentContainer(
     }
   `,
 );
+
+const styleSheet = StyleSheet.create({
+  wrapper: {
+    backgroundColor: Color.white,
+    padding: 10,
+  },
+});
diff --git a/app/MMB/src/scenes/timeline/Timeline.js b/app/MMB/src/scenes/timeline/Timeline.js
index a7b91318..493a8799 100644
--- a/app/MMB/src/scenes/timeline/Timeline.js
+++ b/app/MMB/src/scenes/timeline/Timeline.js
@@ -4,6 +4,7 @@ import * as React from 'react';
 import { ScrollView } from 'react-native';
 import idx from 'idx';
 import { PrivateApiRenderer, graphql } from '@kiwicom/mobile-relay';
+import { DateUtils } from '@kiwicom/mobile-localization';
 
 import type { TimelineQueryResponse } from './__generated__/TimelineQuery.graphql';
 import BookingDetailContext from '../../context/BookingDetailContext';
@@ -13,30 +14,15 @@ import LeaveForAirportTimelineEvent from './events/LeaveForAirportTimelineEvent'
 import DaySeparator from './DaySeparator';
 
 // @TODO Display day banners only once for each day
-function getValidTimelineEvent(data, index) {
+function getValidTimelineEvent(data) {
   if (data && data.__typename) {
     switch (data.__typename) {
       case 'BookedFlightTimelineEvent':
-        return (
-          <React.Fragment key={'TimelineEvent-' + data.__typename + index}>
-            {data && data.timestamp && <DaySeparator date={data.timestamp} />}
-            <BookedFlightTimelineEvent data={data} />
-          </React.Fragment>
-        );
+        return <BookedFlightTimelineEvent data={data} />;
       case 'AirportArrivalTimelineEvent':
-        return (
-          <React.Fragment key={'TimelineEvent-' + data.__typename + index}>
-            {data && data.timestamp && <DaySeparator date={data.timestamp} />}
-            <AirportArrivalTimelineEvent data={data} />
-          </React.Fragment>
-        );
+        return <AirportArrivalTimelineEvent data={data} />;
       case 'LeaveForAirportTimelineEvent':
-        return (
-          <React.Fragment key={'TimelineEvent-' + data.__typename + index}>
-            {data && data.timestamp && <DaySeparator date={data.timestamp} />}
-            <LeaveForAirportTimelineEvent data={data} />
-          </React.Fragment>
-        );
+        return <LeaveForAirportTimelineEvent data={data} />;
       default:
         return null;
     }
@@ -44,13 +30,43 @@ function getValidTimelineEvent(data, index) {
   return null;
 }
 
+function renderDaySeparator(event, prevEvent) {
+  if (event && event.timestamp && prevEvent && prevEvent.timestamp) {
+    const eventTimestamp = new Date(event.timestamp);
+    const prevEventTimestamp = new Date(prevEvent.timestamp);
+    if (!DateUtils.isSameDay(eventTimestamp, prevEventTimestamp)) {
+      return <DaySeparator date={eventTimestamp} />;
+    }
+  }
+  return null;
+}
+
+function renderChildren(events) {
+  if (events) {
+    return events.map((event, index) => {
+      if (event && event.timestamp && event.__typename) {
+        let daySeparator;
+        if (index === 0) {
+          daySeparator = <DaySeparator date={new Date(event.timestamp)} />;
+        } else {
+          daySeparator = renderDaySeparator(event, events[index - 1]);
+        }
+        return (
+          <React.Fragment key={'TimelineEvent-' + event.__typename + index}>
+            {daySeparator}
+            {getValidTimelineEvent(event)}
+          </React.Fragment>
+        );
+      }
+      return null;
+    });
+  }
+}
+
 export default class Timeline extends React.Component<{||}> {
   renderInner = (renderProps: TimelineQueryResponse) => {
     const events = idx(renderProps, _ => _.bookingTimeline.events);
-    const children =
-      events &&
-      events.map((event, index) => getValidTimelineEvent(event, index));
-
+    const children = renderChildren(events);
     return <ScrollView>{children}</ScrollView>;
   };
 
diff --git a/app/MMB/src/scenes/timeline/__generated__/TimelineQuery.graphql.js b/app/MMB/src/scenes/timeline/__generated__/TimelineQuery.graphql.js
index f6b9bcc3..88fb3d09 100644
--- a/app/MMB/src/scenes/timeline/__generated__/TimelineQuery.graphql.js
+++ b/app/MMB/src/scenes/timeline/__generated__/TimelineQuery.graphql.js
@@ -1,6 +1,6 @@
 /**
  * @flow
- * @relayHash 3df0397d6f46f39ff8c7224bd950a04b
+ * @relayHash d63faacc4932f7d15c167e0b667eee6b
  */
 
 /* eslint-disable */
@@ -50,6 +50,14 @@ query TimelineQuery(
 
 fragment BookedFlightTimelineEvent on BookedFlightTimelineEvent {
   timestamp
+  location {
+    airport {
+      city {
+        name
+      }
+      id
+    }
+  }
 }
 
 fragment LeaveForAirportTimelineEvent on LeaveForAirportTimelineEvent {
@@ -97,13 +105,27 @@ v3 = {
   "name": "timestamp",
   "args": null,
   "storageKey": null
+},
+v4 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "name",
+  "args": null,
+  "storageKey": null
+},
+v5 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "id",
+  "args": null,
+  "storageKey": null
 };
 return {
   "kind": "Request",
   "operationKind": "query",
   "name": "TimelineQuery",
   "id": null,
-  "text": "query TimelineQuery(\n  $id: ID!\n) {\n  bookingTimeline(id: $id) {\n    events {\n      __typename\n      timestamp\n      ... on BookedFlightTimelineEvent {\n        ...BookedFlightTimelineEvent\n      }\n      ... on LeaveForAirportTimelineEvent {\n        ...LeaveForAirportTimelineEvent\n      }\n      ... on AirportArrivalTimelineEvent {\n        ...AirportArrivalTimelineEvent\n      }\n    }\n  }\n}\n\nfragment BookedFlightTimelineEvent on BookedFlightTimelineEvent {\n  timestamp\n}\n\nfragment LeaveForAirportTimelineEvent on LeaveForAirportTimelineEvent {\n  timestamp\n}\n\nfragment AirportArrivalTimelineEvent on AirportArrivalTimelineEvent {\n  timestamp\n  location {\n    airport {\n      name\n      id\n    }\n  }\n}\n",
+  "text": "query TimelineQuery(\n  $id: ID!\n) {\n  bookingTimeline(id: $id) {\n    events {\n      __typename\n      timestamp\n      ... on BookedFlightTimelineEvent {\n        ...BookedFlightTimelineEvent\n      }\n      ... on LeaveForAirportTimelineEvent {\n        ...LeaveForAirportTimelineEvent\n      }\n      ... on AirportArrivalTimelineEvent {\n        ...AirportArrivalTimelineEvent\n      }\n    }\n  }\n}\n\nfragment BookedFlightTimelineEvent on BookedFlightTimelineEvent {\n  timestamp\n  location {\n    airport {\n      city {\n        name\n      }\n      id\n    }\n  }\n}\n\nfragment LeaveForAirportTimelineEvent on LeaveForAirportTimelineEvent {\n  timestamp\n}\n\nfragment AirportArrivalTimelineEvent on AirportArrivalTimelineEvent {\n  timestamp\n  location {\n    airport {\n      name\n      id\n    }\n  }\n}\n",
   "metadata": {},
   "fragment": {
     "kind": "Fragment",
@@ -199,6 +221,36 @@ return {
               {
                 "kind": "InlineFragment",
                 "type": "AirportArrivalTimelineEvent",
+                "selections": [
+                  {
+                    "kind": "LinkedField",
+                    "alias": null,
+                    "name": "location",
+                    "storageKey": null,
+                    "args": null,
+                    "concreteType": "RouteStop",
+                    "plural": false,
+                    "selections": [
+                      {
+                        "kind": "LinkedField",
+                        "alias": null,
+                        "name": "airport",
+                        "storageKey": null,
+                        "args": null,
+                        "concreteType": "Location",
+                        "plural": false,
+                        "selections": [
+                          v4,
+                          v5
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              },
+              {
+                "kind": "InlineFragment",
+                "type": "BookedFlightTimelineEvent",
                 "selections": [
                   {
                     "kind": "LinkedField",
@@ -219,19 +271,18 @@ return {
                         "plural": false,
                         "selections": [
                           {
-                            "kind": "ScalarField",
+                            "kind": "LinkedField",
                             "alias": null,
-                            "name": "name",
+                            "name": "city",
+                            "storageKey": null,
                             "args": null,
-                            "storageKey": null
+                            "concreteType": "LocationArea",
+                            "plural": false,
+                            "selections": [
+                              v4
+                            ]
                           },
-                          {
-                            "kind": "ScalarField",
-                            "alias": null,
-                            "name": "id",
-                            "args": null,
-                            "storageKey": null
-                          }
+                          v5
                         ]
                       }
                     ]
diff --git a/app/MMB/src/scenes/timeline/events/BookedFlightTimelineEvent.js b/app/MMB/src/scenes/timeline/events/BookedFlightTimelineEvent.js
index 8cb7cf26..a29fcd24 100644
--- a/app/MMB/src/scenes/timeline/events/BookedFlightTimelineEvent.js
+++ b/app/MMB/src/scenes/timeline/events/BookedFlightTimelineEvent.js
@@ -15,6 +15,7 @@ type Props = {|
 
 const BookedFlightTimelineEvent = (props: Props) => {
   const timestamp = idx(props, _ => _.data.timestamp);
+  const destination = idx(props, _ => _.data.location.airport.city.name);
   return (
     <TimelineEvent
       timestamp={timestamp}
@@ -23,7 +24,7 @@ const BookedFlightTimelineEvent = (props: Props) => {
         <Translation
           id="mmb.booking_timeline.event.booked_flight.title"
           values={{
-            destination: 'Wonderland', // TODO in GQL proxy
+            destination,
           }}
         />
       }
@@ -36,6 +37,13 @@ export default createFragmentContainer(
   graphql`
     fragment BookedFlightTimelineEvent on BookedFlightTimelineEvent {
       timestamp
+      location {
+        airport {
+          city {
+            name
+          }
+        }
+      }
     }
   `,
 );
diff --git a/app/MMB/src/scenes/timeline/events/__generated__/BookedFlightTimelineEvent.graphql.js b/app/MMB/src/scenes/timeline/events/__generated__/BookedFlightTimelineEvent.graphql.js
index be3d44f4..201f20a9 100644
--- a/app/MMB/src/scenes/timeline/events/__generated__/BookedFlightTimelineEvent.graphql.js
+++ b/app/MMB/src/scenes/timeline/events/__generated__/BookedFlightTimelineEvent.graphql.js
@@ -12,6 +12,13 @@ import type { FragmentReference } from "relay-runtime";
 declare export opaque type BookedFlightTimelineEvent$ref: FragmentReference;
 export type BookedFlightTimelineEvent = {|
   +timestamp: ?any,
+  +location: ?{|
+    +airport: ?{|
+      +city: ?{|
+        +name: ?string
+      |}
+    |}
+  |},
   +$refType: BookedFlightTimelineEvent$ref,
 |};
 */
@@ -30,9 +37,49 @@ const node/*: ConcreteFragment*/ = {
       "name": "timestamp",
       "args": null,
       "storageKey": null
+    },
+    {
+      "kind": "LinkedField",
+      "alias": null,
+      "name": "location",
+      "storageKey": null,
+      "args": null,
+      "concreteType": "RouteStop",
+      "plural": false,
+      "selections": [
+        {
+          "kind": "LinkedField",
+          "alias": null,
+          "name": "airport",
+          "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
+                }
+              ]
+            }
+          ]
+        }
+      ]
     }
   ]
 };
 // prettier-ignore
-(node/*: any*/).hash = '44d5e557f82e847a9f06b0429480376a';
+(node/*: any*/).hash = '536f67d9fbd3b35f6791eb8c0846388c';
 module.exports = node;
diff --git a/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js b/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
index b3df0925..c950f820 100644
--- a/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
+++ b/app/MMB/src/scenes/tripServices/InsuranceMenuItem.js
@@ -5,7 +5,9 @@ import { TextIcon } from '@kiwicom/mobile-shared';
 import { Translation } from '@kiwicom/mobile-localization';
 import { TodoMenuItem } from '@kiwicom/mobile-navigation';
 
-type Props = {||};
+type Props = {|
+  onOpenInsurance: () => void,
+|};
 
 export default class InsuranceMenuItem extends React.Component<Props> {
   render = () => {
@@ -22,6 +24,7 @@ export default class InsuranceMenuItem extends React.Component<Props> {
           <Translation id="mmb.trip_services.general_services.insurance" />
         }
         icon={<TextIcon code="'" />}
+        onPress={this.props.onOpenInsurance}
       />
     );
   };
diff --git a/app/MMB/src/scenes/tripServices/TripServices.js b/app/MMB/src/scenes/tripServices/TripServices.js
index 550d566f..e90f7912 100644
--- a/app/MMB/src/scenes/tripServices/TripServices.js
+++ b/app/MMB/src/scenes/tripServices/TripServices.js
@@ -17,6 +17,7 @@ import LoungeMenuItem from './LoungeMenuItem';
 import ParkingMenuItem from './ParkingMenuItem';
 import InsuranceMenuItem from './InsuranceMenuItem';
 import CarRentalMenuItem from './CarRentalMenuItem';
+import InsuranceOverview from './insurance/InsuranceOverview';
 import BookingDetailContext from '../../context/BookingDetailContext';
 import type { TripServicesQueryResponse } from './__generated__/TripServicesQuery.graphql';
 
@@ -25,7 +26,7 @@ type Props = {|
 |};
 
 export default class TripServices extends React.Component<Props> {
-  navigate = (key: RouteNamesType, params: Object) => {
+  navigate = (key: RouteNamesType, params?: Object) => {
     this.props.navigation.navigate({
       routeName: key,
       key: `key-${key}`,
@@ -39,6 +40,10 @@ export default class TripServices extends React.Component<Props> {
     });
   };
 
+  openInsurance = () => {
+    this.navigate('mmb.trip_services.insurance');
+  };
+
   renderLocalServices = (rendererProps: TripServicesQueryResponse) => {
     const availableWhitelabeledServices = idx(
       rendererProps,
@@ -52,7 +57,7 @@ export default class TripServices extends React.Component<Props> {
         <TitledMenuGroup
           title={<Translation id="mmb.trip_services.general_services" />}
         >
-          <InsuranceMenuItem />
+          <InsuranceMenuItem onOpenInsurance={this.openInsurance} />
         </TitledMenuGroup>
 
         <TitledMenuGroup
@@ -118,4 +123,9 @@ export const TripServicesSubmenuItems = {
       return <WebView source={{ uri: url }} />;
     },
   },
+  'mmb.trip_services.insurance': {
+    screen: function TripServicesSubmenuInsurance() {
+      return <InsuranceOverview />;
+    },
+  },
 };
diff --git a/app/MMB/src/scenes/tripServices/insurance/DestinationImage.js b/app/MMB/src/scenes/tripServices/insurance/DestinationImage.js
new file mode 100644
index 00000000..cfa61f6e
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/insurance/DestinationImage.js
@@ -0,0 +1,44 @@
+// @flow strict
+
+import * as React from 'react';
+import { View } from 'react-native';
+import { StyleSheet, NetworkImage, Color } from '@kiwicom/mobile-shared';
+import { graphql, createFragmentContainer } from '@kiwicom/mobile-relay';
+import idx from 'idx';
+
+import type { DestinationImage as DestinationImageType } from './__generated__/DestinationImage.graphql';
+
+type Props = {|
+  +data: DestinationImageType,
+|};
+
+const DestinationImage = (props: Props) => (
+  <View style={styles.container}>
+    <NetworkImage
+      source={{
+        uri: idx(props.data, _ => _.destinationImageUrl),
+      }}
+      style={styles.image}
+      resizeMode="cover"
+    />
+  </View>
+);
+
+export default createFragmentContainer(
+  DestinationImage,
+  graphql`
+    fragment DestinationImage on BookingInterface {
+      destinationImageUrl(dimensions: _375x165)
+    }
+  `,
+);
+
+const styles = StyleSheet.create({
+  image: StyleSheet.absoluteFillObject,
+  container: {
+    backgroundColor: Color.brandLight,
+    height: 150,
+    width: '100%',
+    overflow: 'hidden',
+  },
+});
diff --git a/app/MMB/src/scenes/tripServices/insurance/InsuranceOverview.js b/app/MMB/src/scenes/tripServices/insurance/InsuranceOverview.js
new file mode 100644
index 00000000..d54e946d
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/insurance/InsuranceOverview.js
@@ -0,0 +1,44 @@
+// @flow strict
+
+import * as React from 'react';
+import { LayoutSingleColumn } from '@kiwicom/mobile-shared';
+import { graphql, PrivateApiRenderer } from '@kiwicom/mobile-relay';
+
+import DestinationImage from './DestinationImage';
+import TripInfo from '../../../components/header/TripInfo';
+import BookingDetailContext from '../../../context/BookingDetailContext';
+import type { InsuranceOverviewQueryResponse } from './__generated__/InsuranceOverviewQuery.graphql';
+
+function InsuranceOverview(response: InsuranceOverviewQueryResponse) {
+  return (
+    <LayoutSingleColumn>
+      <DestinationImage data={response.node} />
+      <TripInfo data={response.node} />
+    </LayoutSingleColumn>
+  );
+}
+
+export default function InsuranceOverviewContainer() {
+  return (
+    <BookingDetailContext.Consumer>
+      {({ bookingId }) => (
+        <PrivateApiRenderer
+          render={InsuranceOverview}
+          query={graphql`
+            query InsuranceOverviewQuery($bookingId: ID!) {
+              node(id: $bookingId) {
+                ... on BookingInterface {
+                  ...DestinationImage
+                  ...TripInfo
+                }
+              }
+            }
+          `}
+          variables={{
+            bookingId: bookingId,
+          }}
+        />
+      )}
+    </BookingDetailContext.Consumer>
+  );
+}
diff --git a/app/MMB/src/scenes/tripServices/insurance/__generated__/DestinationImage.graphql.js b/app/MMB/src/scenes/tripServices/insurance/__generated__/DestinationImage.graphql.js
new file mode 100644
index 00000000..ba1c26d3
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/insurance/__generated__/DestinationImage.graphql.js
@@ -0,0 +1,45 @@
+/**
+ * @flow
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ConcreteFragment } from 'relay-runtime';
+import type { FragmentReference } from "relay-runtime";
+declare export opaque type DestinationImage$ref: FragmentReference;
+export type DestinationImage = {|
+  +destinationImageUrl: ?string,
+  +$refType: DestinationImage$ref,
+|};
+*/
+
+
+const node/*: ConcreteFragment*/ = {
+  "kind": "Fragment",
+  "name": "DestinationImage",
+  "type": "BookingInterface",
+  "metadata": null,
+  "argumentDefinitions": [],
+  "selections": [
+    {
+      "kind": "ScalarField",
+      "alias": null,
+      "name": "destinationImageUrl",
+      "args": [
+        {
+          "kind": "Literal",
+          "name": "dimensions",
+          "value": "_375x165",
+          "type": "BookingDestinationImageDimensions"
+        }
+      ],
+      "storageKey": "destinationImageUrl(dimensions:\"_375x165\")"
+    }
+  ]
+};
+// prettier-ignore
+(node/*: any*/).hash = '9b557a759a43f489da10a072a6ca01a7';
+module.exports = node;
diff --git a/app/MMB/src/scenes/tripServices/insurance/__generated__/InsuranceOverviewQuery.graphql.js b/app/MMB/src/scenes/tripServices/insurance/__generated__/InsuranceOverviewQuery.graphql.js
new file mode 100644
index 00000000..f585a89c
--- /dev/null
+++ b/app/MMB/src/scenes/tripServices/insurance/__generated__/InsuranceOverviewQuery.graphql.js
@@ -0,0 +1,387 @@
+/**
+ * @flow
+ * @relayHash a5323ba0491d91324cb89ac9aaf78f12
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ConcreteRequest } from 'relay-runtime';
+type DestinationImage$ref = any;
+type TripInfo$ref = any;
+export type InsuranceOverviewQueryVariables = {|
+  bookingId: string
+|};
+export type InsuranceOverviewQueryResponse = {|
+  +node: ?{|
+    +$fragmentRefs: DestinationImage$ref & TripInfo$ref
+  |}
+|};
+*/
+
+
+/*
+query InsuranceOverviewQuery(
+  $bookingId: ID!
+) {
+  node(id: $bookingId) {
+    __typename
+    ... on BookingInterface {
+      ...DestinationImage
+      ...TripInfo
+    }
+    id
+  }
+}
+
+fragment DestinationImage on BookingInterface {
+  destinationImageUrl(dimensions: _375x165)
+}
+
+fragment TripInfo on BookingInterface {
+  __typename
+  ... on BookingOneWay {
+    ...TripInfoOneWay
+  }
+  ... on BookingReturn {
+    ...TripInfoReturn
+  }
+  ... on BookingMulticity {
+    ...TripInfoMulticity
+  }
+}
+
+fragment TripInfoOneWay on BookingOneWay {
+  trip {
+    ...TripCities
+    ...TripTimes
+  }
+}
+
+fragment TripInfoReturn on BookingReturn {
+  outbound {
+    ...TripCities
+    ...TripTimes
+  }
+  inbound {
+    ...TripTimes
+  }
+}
+
+fragment TripInfoMulticity on BookingMulticity {
+  trips {
+    ...TripCities
+    ...TripTimes
+  }
+}
+
+fragment TripCities on Trip {
+  departure {
+    ...Location
+  }
+  arrival {
+    ...Location
+  }
+}
+
+fragment TripTimes on Trip {
+  ...Duration
+  departure {
+    ...DateTime
+  }
+  arrival {
+    ...DateTime
+  }
+}
+
+fragment Duration on Trip {
+  duration
+}
+
+fragment DateTime on RouteStop {
+  localTime
+}
+
+fragment Location on RouteStop {
+  airport {
+    city {
+      name
+    }
+    ...CountryFlag
+    id
+  }
+}
+
+fragment CountryFlag on Location {
+  countryFlagURL
+}
+*/
+
+const node/*: ConcreteRequest*/ = (function(){
+var v0 = [
+  {
+    "kind": "LocalArgument",
+    "name": "bookingId",
+    "type": "ID!",
+    "defaultValue": null
+  }
+],
+v1 = [
+  {
+    "kind": "Variable",
+    "name": "id",
+    "variableName": "bookingId",
+    "type": "ID!"
+  }
+],
+v2 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "id",
+  "args": null,
+  "storageKey": null
+},
+v3 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "localTime",
+  "args": null,
+  "storageKey": null
+},
+v4 = [
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "airport",
+    "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": "countryFlagURL",
+        "args": null,
+        "storageKey": null
+      },
+      v2
+    ]
+  },
+  v3
+],
+v5 = {
+  "kind": "ScalarField",
+  "alias": null,
+  "name": "duration",
+  "args": null,
+  "storageKey": null
+},
+v6 = [
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "departure",
+    "storageKey": null,
+    "args": null,
+    "concreteType": "RouteStop",
+    "plural": false,
+    "selections": v4
+  },
+  {
+    "kind": "LinkedField",
+    "alias": null,
+    "name": "arrival",
+    "storageKey": null,
+    "args": null,
+    "concreteType": "RouteStop",
+    "plural": false,
+    "selections": v4
+  },
+  v5
+],
+v7 = [
+  v3
+];
+return {
+  "kind": "Request",
+  "operationKind": "query",
+  "name": "InsuranceOverviewQuery",
+  "id": null,
+  "text": "query InsuranceOverviewQuery(\n  $bookingId: ID!\n) {\n  node(id: $bookingId) {\n    __typename\n    ... on BookingInterface {\n      ...DestinationImage\n      ...TripInfo\n    }\n    id\n  }\n}\n\nfragment DestinationImage on BookingInterface {\n  destinationImageUrl(dimensions: _375x165)\n}\n\nfragment TripInfo on BookingInterface {\n  __typename\n  ... on BookingOneWay {\n    ...TripInfoOneWay\n  }\n  ... on BookingReturn {\n    ...TripInfoReturn\n  }\n  ... on BookingMulticity {\n    ...TripInfoMulticity\n  }\n}\n\nfragment TripInfoOneWay on BookingOneWay {\n  trip {\n    ...TripCities\n    ...TripTimes\n  }\n}\n\nfragment TripInfoReturn on BookingReturn {\n  outbound {\n    ...TripCities\n    ...TripTimes\n  }\n  inbound {\n    ...TripTimes\n  }\n}\n\nfragment TripInfoMulticity on BookingMulticity {\n  trips {\n    ...TripCities\n    ...TripTimes\n  }\n}\n\nfragment TripCities on Trip {\n  departure {\n    ...Location\n  }\n  arrival {\n    ...Location\n  }\n}\n\nfragment TripTimes on Trip {\n  ...Duration\n  departure {\n    ...DateTime\n  }\n  arrival {\n    ...DateTime\n  }\n}\n\nfragment Duration on Trip {\n  duration\n}\n\nfragment DateTime on RouteStop {\n  localTime\n}\n\nfragment Location on RouteStop {\n  airport {\n    city {\n      name\n    }\n    ...CountryFlag\n    id\n  }\n}\n\nfragment CountryFlag on Location {\n  countryFlagURL\n}\n",
+  "metadata": {},
+  "fragment": {
+    "kind": "Fragment",
+    "name": "InsuranceOverviewQuery",
+    "type": "RootQuery",
+    "metadata": null,
+    "argumentDefinitions": v0,
+    "selections": [
+      {
+        "kind": "LinkedField",
+        "alias": null,
+        "name": "node",
+        "storageKey": null,
+        "args": v1,
+        "concreteType": null,
+        "plural": false,
+        "selections": [
+          {
+            "kind": "FragmentSpread",
+            "name": "DestinationImage",
+            "args": null
+          },
+          {
+            "kind": "FragmentSpread",
+            "name": "TripInfo",
+            "args": null
+          }
+        ]
+      }
+    ]
+  },
+  "operation": {
+    "kind": "Operation",
+    "name": "InsuranceOverviewQuery",
+    "argumentDefinitions": v0,
+    "selections": [
+      {
+        "kind": "LinkedField",
+        "alias": null,
+        "name": "node",
+        "storageKey": null,
+        "args": v1,
+        "concreteType": null,
+        "plural": false,
+        "selections": [
+          {
+            "kind": "ScalarField",
+            "alias": null,
+            "name": "destinationImageUrl",
+            "args": [
+              {
+                "kind": "Literal",
+                "name": "dimensions",
+                "value": "_375x165",
+                "type": "BookingDestinationImageDimensions"
+              }
+            ],
+            "storageKey": "destinationImageUrl(dimensions:\"_375x165\")"
+          },
+          {
+            "kind": "ScalarField",
+            "alias": null,
+            "name": "__typename",
+            "args": null,
+            "storageKey": null
+          },
+          v2,
+          {
+            "kind": "InlineFragment",
+            "type": "BookingMulticity",
+            "selections": [
+              {
+                "kind": "LinkedField",
+                "alias": null,
+                "name": "trips",
+                "storageKey": null,
+                "args": null,
+                "concreteType": "Trip",
+                "plural": true,
+                "selections": v6
+              }
+            ]
+          },
+          {
+            "kind": "InlineFragment",
+            "type": "BookingReturn",
+            "selections": [
+              {
+                "kind": "LinkedField",
+                "alias": null,
+                "name": "outbound",
+                "storageKey": null,
+                "args": null,
+                "concreteType": "Trip",
+                "plural": false,
+                "selections": v6
+              },
+              {
+                "kind": "LinkedField",
+                "alias": null,
+                "name": "inbound",
+                "storageKey": null,
+                "args": null,
+                "concreteType": "Trip",
+                "plural": false,
+                "selections": [
+                  v5,
+                  {
+                    "kind": "LinkedField",
+                    "alias": null,
+                    "name": "departure",
+                    "storageKey": null,
+                    "args": null,
+                    "concreteType": "RouteStop",
+                    "plural": false,
+                    "selections": v7
+                  },
+                  {
+                    "kind": "LinkedField",
+                    "alias": null,
+                    "name": "arrival",
+                    "storageKey": null,
+                    "args": null,
+                    "concreteType": "RouteStop",
+                    "plural": false,
+                    "selections": v7
+                  }
+                ]
+              }
+            ]
+          },
+          {
+            "kind": "InlineFragment",
+            "type": "BookingOneWay",
+            "selections": [
+              {
+                "kind": "LinkedField",
+                "alias": null,
+                "name": "trip",
+                "storageKey": null,
+                "args": null,
+                "concreteType": "Trip",
+                "plural": false,
+                "selections": v6
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+};
+})();
+// prettier-ignore
+(node/*: any*/).hash = '122c9b2d75a21bde26d6e9dfd0454193';
+module.exports = node;
diff --git a/packages/localization/src/DateUtils.js b/packages/localization/src/DateUtils.js
index 8d1ad4d4..21c7b247 100644
--- a/packages/localization/src/DateUtils.js
+++ b/packages/localization/src/DateUtils.js
@@ -25,4 +25,12 @@ DateUtils.diffInDays = (higherDate: Date, lowerDate: Date) => {
   return Math.ceil(diffMs / (1000 * 3600 * 24));
 };
 
+DateUtils.isSameDay = (firstDate: Date, secondDate: Date): boolean => {
+  return (
+    firstDate.getUTCFullYear() === secondDate.getUTCFullYear() &&
+    firstDate.getUTCMonth() === secondDate.getUTCMonth() &&
+    firstDate.getUTCDate() === secondDate.getUTCDate()
+  );
+};
+
 export default DateUtils;
diff --git a/packages/navigation/types/Navigation.js b/packages/navigation/types/Navigation.js
index 5f00910c..586aba57 100644
--- a/packages/navigation/types/Navigation.js
+++ b/packages/navigation/types/Navigation.js
@@ -26,6 +26,7 @@ type MMBStackNavigatorNames =
   | 'mmb.flight_services.pets'
   | 'mmb.flight_services.special_assistance'
   | 'mmb.flight_services.sports_equipment'
+  | 'mmb.trip_services.insurance'
   | 'mmb.trip_services.webview'
   | 'mmb.help.help'
   | 'mmb.help.support'
diff --git a/schema.graphql b/schema.graphql
index 241747b3..26a33711 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -101,6 +101,9 @@ type BoardingTimelineEvent implements TimelineEvent {
 type BookedFlightTimelineEvent implements TimelineEvent {
   """Time of appearance in the timeline."""
   timestamp: DateTime
+
+  """Location of arrival"""
+  location: RouteStop
 }
 
 type BookedService {


Test patch:
diff --git a/packages/localization/src/__tests__/DateUtils.test.js b/packages/localization/src/__tests__/DateUtils.test.js
index c2000f2d..111cdeba 100644
--- a/packages/localization/src/__tests__/DateUtils.test.js
+++ b/packages/localization/src/__tests__/DateUtils.test.js
@@ -31,3 +31,26 @@ it('returns correct diff in days', () => {
     DateUtils.diffInDays(new Date('2018-12-24'), new Date('2018-12-30')),
   ).toBe(-6);
 });
+
+describe('isSameDay', () => {
+  it('returns the right answer', () => {
+    expect(
+      DateUtils.isSameDay(new Date('2018-12-24'), new Date('2018-12-24')),
+    ).toBe(true);
+    expect(
+      DateUtils.isSameDay(
+        new Date('2018-12-24T00:00:00.000+02:00'),
+        new Date('2018-12-23T23:00:00.000Z'),
+      ),
+    ).toBe(true);
+    expect(
+      DateUtils.isSameDay(
+        new Date('2019-01-01T00:00:00.000+02:00'),
+        new Date('2018-12-31T23:00:00.000Z'),
+      ),
+    ).toBe(true);
+    expect(
+      DateUtils.isSameDay(new Date('2018-12-24'), new Date('2018-12-23')),
+    ).toBe(false);
+  });
+});