Covivo/mobicoop

View on GitHub
client/src/MobicoopBundle/Resources/assets/js/components/carpool/publish/AdPublish.vue

Summary

Maintainability
Test Coverage
<template>
  <v-container fluid>
    <!--prevent user to update data before full initialization-->
    <v-row
      v-if="isValidUpdate && !bodyIsFullyLoaded"
      id="loading-screen"
      justify="center"
      align="center"
    >
      <div class="text-center">
        <v-progress-circular
          :indeterminate="true"
          :rotate="0"
          :size="48"
          color="primary"
        />
      </div>
    </v-row>
    <v-snackbar
      v-model="snackbar.show"
      :color="snackbar.color"
      top
    >
      {{ snackbar.message }}
      <v-btn
        color="white"
        text
        @click="snackbar.show = false"
      >
        <v-icon>mdi-close-circle-outline</v-icon>
      </v-btn>
    </v-snackbar>

    <v-snackbar
      v-model="snackErrorPublish.show"
      :color="snackErrorPublish.color"
      top
      timeout="-1"
    >
      {{ snackErrorPublishMessage }}
      <v-btn
        color="white"
        text
        @click="snackErrorPublish.show = false"
      >
        <v-icon>mdi-close-circle-outline</v-icon>
      </v-btn>
    </v-snackbar>

    <!-- Title and subtitle -->
    <v-row
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
        align="center"
      >
        <h1>{{ $t( isUpdate && !isSearchToSave ? 'update.title' : 'create.title') }}</h1>
        <h3 style="height: 30px">
          {{ step === 1 && !isUpdate ? $t('create.subtitle') : "" }}
        </h3>
      </v-col>
    </v-row>
    <v-row
      v-if="solidaryExclusiveAd"
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
      >
        <v-alert type="info">
          <p>{{ $t("messageSolidaryExclusiveAd.message") }}</p>
        </v-alert>
      </v-col>
    </v-row>
    <v-row
      v-if="solidaryExclusiveAd"
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
        class="d-flex justify-center"
      >
        <v-switch
          v-model="solidaryExclusive"
          color="success"
          inset
          :label="$t('messageSolidaryExclusiveAd.switch.label')"
        />
      </v-col>
    </v-row>
    <v-row
      v-if="firstAd"
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
      >
        <v-alert type="info">
          <p>{{ $t("messageFirstAd.signUpDone", {'givenName':user.givenName}) }}</p>
          <p>{{ $t("messageFirstAd.alert") }}</p>
        </v-alert>
      </v-col>
    </v-row>
    <v-row
      v-if="!user.oldEnoughToDrive"
      justify="center"
    >
      <v-col
        cols="7"
        xl="8"
      >
        <v-alert type="info">
          <p>{{ $t("tooYoungToDrive") }}</p>
        </v-alert>
      </v-col>
    </v-row>
    <v-row
      v-if="isSearchToSave"
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
      >
        <v-alert type="info">
          <p>{{ $t("searchToSave.messageTitle") }}</p>
          <p>{{ $t("searchToSave.message") }}</p>
        </v-alert>
      </v-col>
    </v-row>
    <v-row
      v-if="isUpdate && hasAsks"
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
      >
        <v-alert type="info">
          <p>{{ $t("update.informativeMessage") }}</p>
        </v-alert>
      </v-col>
    </v-row>
    <!-- Stepper -->
    <v-row
      justify="center"
    >
      <v-col
        cols="12"
        xl="8"
        align="center"
      >
        <v-stepper
          v-model="step"
          alt-labels
          class="elevation-0"
        >
          <!-- Stepper Header -->
          <v-stepper-header
            v-show="step!==1 || isUpdate"
            class="elevation-0"
          >
            <!-- Step 1 : search journey -->
            <v-stepper-step
              complete
              editable
              :step="1"
              color="primary"
            >
              {{ $t('stepper.header.search_journey') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 2 : planification -->
            <v-stepper-step
              editable
              :step="2"
              color="primary"
            >
              {{ $t('stepper.header.planification') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 3 : map -->
            <v-stepper-step
              editable
              :step="3"
              color="primary"
            >
              {{ $t('stepper.header.map') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 4 : passengers (if driver) -->
            <v-stepper-step
              v-if="driver"
              editable
              :step="4"
              color="primary"
            >
              {{ $t('stepper.header.passengers') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 5 : participation (if driver) -->
            <v-stepper-step
              v-if="driver && !solidaryExclusive"
              editable
              :step="5"
              color="primary"
            >
              {{ $t('stepper.header.participation') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 6 : message -->
            <v-stepper-step
              editable
              :step="driver ? (solidaryExclusive ? 5 : 6) : 4"
              color="primary"
            >
              {{ $t('stepper.header.message') }}
            </v-stepper-step>
            <v-divider />

            <!-- Step 7 : summary -->
            <v-stepper-step
              color="primary"
              editable
              :step="driver ? (solidaryExclusive ? 6 : 7) : 5"
            >
              {{ $t('stepper.header.summary') }}
            </v-stepper-step>
          </v-stepper-header>

          <!-- Stepper Content -->
          <v-stepper-items>
            <!-- Step 1 : search journey -->
            <v-stepper-content step="1">
              <search-journey
                :solidary-exclusive-ad="solidaryExclusive"
                display-roles
                :geo-search-url="geoSearchUrl"
                :geo-complete-results-order="geoCompleteResultsOrder"
                :geo-complete-palette="geoCompletePalette"
                :geo-complete-chip="geoCompleteChip"
                :user="user"
                :init-outward-date="outwardDate"
                :init-origin="origin"
                :init-destination="destination"
                :init-regular="regular"
                :init-role="role"
                :driver-role-enabled="showDriverRole"
                :both-role-enabled="showBothRole"
                @change="searchChanged"
              />
            </v-stepper-content>

            <!-- Step 2 : planification -->
            <v-stepper-content step="2">
              <ad-planification
                :init-outward-date="outwardDate"
                :init-outward-time="outwardTime"
                :init-return-date="returnDate"
                :init-return-time="returnTime"
                :regular="regular"
                :default-margin-duration="defaultMarginDuration"
                :default-time-precision="defaultTimePrecision"
                :init-schedule="initSchedule"
                :route="route"
                :staggered-schedules-allowed="staggeredSchedulesAllowed"
                :default-digital-clock="defaultDigitalClock"
                @change="planificationChanged"
              />
            </v-stepper-content>

            <!-- Step 3 : route -->
            <v-stepper-content step="3">
              <v-row>
                <v-col cols="6">
                  <m-map
                    ref="mmapRoute"
                    type-map="adSummary"
                    :points="pointsToMap"
                    :ways="directionWay"
                    :provider="mapProvider"
                    :url-tiles="urlTiles"
                    :attribution-copyright="attributionCopyright"
                  />
                </v-col>
                <v-col cols="6">
                  <ad-route
                    :geo-search-url="geoSearchUrl"
                    :geo-route-url="geoRouteUrl"
                    :geo-complete-results-order="geoCompleteResultsOrder"
                    :geo-complete-palette="geoCompletePalette"
                    :geo-complete-chip="geoCompleteChip"
                    :user="user"
                    :init-origin="origin"
                    :init-destination="destination"
                    :init-waypoints="initWaypoints"
                    :community-ids="communityIds"
                    @change="routeChanged"
                  />
                </v-col>
              </v-row>
            </v-stepper-content>

            <!-- Step 4 : passengers (if driver) -->
            <v-stepper-content
              v-if="driver"
              step="4"
            >
              <v-row
                dense
                align="center"
                justify="center"
              >
                <v-col
                  cols="3"
                  align="right"
                >
                  {{ $t('stepper.content.passengers.seats.question') }}
                </v-col>

                <v-col
                  cols="1"
                >
                  <v-select
                    v-model="seats"
                    :items="itemsSeatNumber"
                    item-text="text"
                    item-value="value"
                  />
                </v-col>

                <v-col
                  cols="2"
                  align="left"
                >
                  {{ $t('stepper.content.passengers.seats.passengers') }}
                </v-col>
              </v-row>
              <div v-if="contentPassenger">
                <v-row
                  align="center"
                  dense
                >
                  <v-col
                    cols="5"
                    offset="3"
                    align="left"
                  >
                    {{ $t('stepper.content.passengers.luggage.label') }}
                  </v-col>
                  <v-col
                    cols="1"
                  >
                    <v-switch
                      v-model="luggage"
                      inset
                      hide-details
                      class="mt-0 mb-1"
                      color="primary"
                    />
                  </v-col>
                  <v-col
                    cols="1"
                    align="left"
                  >
                    <v-tooltip
                      right
                      color="info"
                    >
                      <template v-slot:activator="{ on }">
                        <v-icon v-on="on">
                          mdi-help-circle-outline
                        </v-icon>
                      </template>
                      <span>{{ $t('stepper.content.passengers.luggage.help') }}</span>
                    </v-tooltip>
                  </v-col>
                </v-row>

                <v-row
                  align="center"
                  dense
                >
                  <v-col
                    cols="5"
                    offset="3"
                    align="left"
                  >
                    {{ $t('stepper.content.passengers.bike.label') }}
                  </v-col>
                  <v-col
                    cols="1"
                  >
                    <v-switch
                      v-model="bike"
                      inset
                      hide-details
                      class="mt-0 mb-1"
                      color="primary"
                    />
                  </v-col>
                  <v-col
                    cols="1"
                    align="left"
                  >
                    <v-tooltip
                      right
                      color="info"
                    >
                      <template v-slot:activator="{ on }">
                        <v-icon v-on="on">
                          mdi-help-circle-outline
                        </v-icon>
                      </template>
                      <span>{{ $t('stepper.content.passengers.bike.help') }}</span>
                    </v-tooltip>
                  </v-col>
                </v-row>

                <v-row
                  align="center"
                  dense
                >
                  <v-col
                    cols="5"
                    offset="3"
                    align="left"
                  >
                    {{ $t('stepper.content.passengers.backSeats.label') }}
                  </v-col>
                  <v-col
                    cols="1"
                  >
                    <v-switch
                      v-model="backSeats"
                      inset
                      hide-details
                      class="mt-0 mb-1"
                      color="primary"
                    />
                  </v-col>
                  <v-col
                    cols="1"
                    align="left"
                  >
                    <v-tooltip
                      color="info"
                      right
                    >
                      <template v-slot:activator="{ on }">
                        <v-icon v-on="on">
                          mdi-help-circle-outline
                        </v-icon>
                      </template>
                      <span>{{ $t('stepper.content.passengers.backSeats.help') }}</span>
                    </v-tooltip>
                  </v-col>
                </v-row>
              </div>
            </v-stepper-content>

            <!-- Step 5 : participation (if driver) -->
            <v-stepper-content
              v-if="driver && !solidaryExclusive"
              step="5"
            >
              <v-row
                align="center"
                justify="center"
              >
                <v-col cols="10">
                  <p>{{ $t('stepper.content.participation.details') }}</p>
                  <p v-if="communityWithFreeCarpool && freeCarpool">
                    <span v-if="freeCarpoolCommunities.length === 1">
                      {{ $t('freeCarpool.one', { communityName: freeCarpoolCommunities[0].name }) }}
                    </span>
                    <span v-else>
                      {{ $t('freeCarpool.many') }}
                    </span>
                  </p>
                </v-col>
              </v-row>
              <v-row
                dense
                align="center"
                justify="center"
              >
                <v-col
                  cols="3"
                  align="right"
                >
                  {{ $t('stepper.content.participation.price') }}
                </v-col>

                <v-col
                  cols="2"
                >
                  <v-text-field
                    v-model="price"
                    :disabled="distance<=0"
                    suffix="€"
                    :hint="hintPricePerKm"
                    persistent-hint
                    :color="colorPricePerKm"
                    :class="colorPricePerKm + '--text'"
                    @input="disableNextButton = true;roundPrice(price, regular ? 2 : 1, true);price = Math.abs(price)"
                  />
                </v-col>

                <v-col
                  cols="2"
                  align="left"
                >
                  {{ $t('stepper.content.participation.passengers') }}
                </v-col>
              </v-row>
              <v-row
                v-if="pricePerKm >= pricesRanges.mid"
                justify="center"
              >
                <v-col cols="10">
                  <v-card>
                    <v-card-text>
                      <p
                        v-if="pricePerKm >= pricesRanges.forbidden"
                        :class="colorPricePerKm + '--text'"
                      >
                        {{ $t('participation.forbidden') }}
                      </p>
                      <p
                        v-else-if="pricePerKm >= pricesRanges.high"
                        :class="colorPricePerKm + '--text'"
                      >
                        {{ $t('participation.high') }}
                      </p>
                      <p
                        v-else-if="pricePerKm >= pricesRanges.mid"
                        :class="colorPricePerKm + '--text'"
                      >
                        {{ $t('participation.mid') }}
                      </p>
                    </v-card-text>
                  </v-card>
                </v-col>
              </v-row>
              <v-row justify="center">
                <v-col
                  v-if="participationText"
                  cols="10"
                  align="center"
                >
                  <p
                    class="text-caption"
                    v-html="$t('participation.text')"
                  />
                </v-col>
              </v-row>
            </v-stepper-content>

            <!-- Step 6 : message -->
            <v-stepper-content
              :step="driver ? (solidaryExclusive ? 5 : 6) : 4"
            >
              <v-row
                dense
                align="center"
                justify="center"
              >
                <v-col
                  cols="6"
                >
                  <p v-if="driver && passenger">
                    {{ $t('stepper.content.message.title.both') }}
                  </p>
                  <p v-else-if="driver">
                    {{ $t('stepper.content.message.title.driver') }}
                  </p>
                  <p v-else>
                    {{ $t('stepper.content.message.title.passenger') }}
                  </p>
                  <v-textarea
                    v-model="message"
                    :label="$t('stepper.content.message.label')"
                  />
                </v-col>
              </v-row>
            </v-stepper-content>

            <!-- Step 7 : summary -->
            <v-stepper-content
              :step="driver ? (solidaryExclusive ? 6 : 7) : 5"
            >
              <v-container>
                <v-row>
                  <v-col cols="12">
                    <ad-summary
                      :driver="driver"
                      :passenger="passenger"
                      :regular="regular"
                      :outward-date="outwardDate"
                      :outward-time="outwardTime"
                      :return-date="returnDate"
                      :return-time="returnTime"
                      :schedules="schedules"
                      :seats="seats"
                      :price="parseFloat(price)"
                      :route="route"
                      :message="message"
                      :user="user"
                      :origin="origin"
                      :destination="destination"
                      :solidary-exclusive="solidaryExclusive"
                      :age-display="ageDisplay"
                    />
                  </v-col>
                </v-row>
                <v-row>
                  <v-col cols="12">
                    <m-map
                      ref="mmapSummary"
                      type-map="adSummary"
                      :points="pointsToMap"
                      :ways="directionWay"
                      :provider="mapProvider"
                      :url-tiles="urlTiles"
                      :attribution-copyright="attributionCopyright"
                    />
                  </v-col>
                </v-row>

                <v-row>
                  <v-col
                    v-if="driver && !specificTerms"
                  >
                    {{ $t('stepper.driverLicense.text') }}
                    <a
                      class="primary--text"
                      target="_blank"
                      :href="$t('stepper.driverLicense.route')"
                      @click.stop
                    >{{ $t('stepper.driverLicense.link') }}
                    </a>
                  </v-col>
                </v-row>
                <v-row
                  v-if="driver && specificTerms"
                >
                  <v-col>
                    <v-checkbox
                      v-model="checkboxDrivingLicence"
                      color="primary"
                      required
                    >
                      <template v-slot:label>
                        <div
                          @click.stop
                          v-html="$t('checkboxes.drivingLicence')"
                        />
                      </template>
                    </v-checkbox>
                    <v-checkbox
                      v-model="checkboxEmployer"
                      color="primary"
                      required
                    >
                      <template v-slot:label>
                        <div
                          @click.stop
                          v-html="$t('checkboxes.employer')"
                        />
                      </template>
                    </v-checkbox>
                    <v-checkbox
                      v-model="checkboxInssurance"
                      color="primary"
                      required
                    >
                      <template v-slot:label>
                        <div
                          @click.stop
                          v-html="$t('checkboxes.inssurance')"
                        />
                      </template>
                    </v-checkbox>
                  </v-col>
                </v-row>
              </v-container>
            </v-stepper-content>
          </v-stepper-items>
        </v-stepper>
      </v-col>
    </v-row>
    <!-- </v-stepper-content> -->

    <!-- Buttons Previous and Next step -->
    <v-row
      mt-5
      justify="center"
    >
      <v-btn
        v-if="step > 1"
        rounded
        outlined
        color="secondary"
        align-center
        style="margin-bottom: 30px;"
        @click="--step; scrollToElement()"
      >
        {{ $t('stepper.buttons.previous') }}
      </v-btn>

      <v-btn
        v-if="(step === 5 && driver && !solidaryExclusive)"
        :disabled="disableNextButton || price < 0"
        :loading="loadingPrice"
        rounded
        color="secondary"
        align-center
        style="margin-left: 30px;"
        @click="++step; scrollToElement()"
      >
        {{ $t('stepper.buttons.next') }}
      </v-btn>

      <v-btn
        v-if="((step < 7 && driver && step !== 5 && !solidaryExclusive) || (step < 5 && !driver && !solidaryExclusive) || (solidaryExclusive && step < 6))"
        :disabled="!validNext"
        rounded
        color="secondary"
        align-center
        style="margin-left: 30px;"
        @click="++step; scrollToElement()"
      >
        {{ $t('stepper.buttons.next') }}
      </v-btn>

      <v-tooltip
        v-if="isMainBtnDisplayed"
        bottom
      >
        <template v-slot:activator="{on}">
          <div v-on="(!valid)?on:{}">
            <v-btn
              :disabled="!valid || loading"
              :loading="loading"
              rounded
              color="secondary"
              style="margin-left: 30px;"
              align-center
              @click="isUpdate ? (hasAsks || hasPotentialAds ? dialog = true : updateAd()) : postAd()"
            >
              {{ isUpdate && !isSearchToSave ? $t('stepper.buttons.update_ad', {id: ad.id}) : $t('stepper.buttons.publish_ad') }}
            </v-btn>
          </div>
        </template>
        <span v-html="getTooltip" />
      </v-tooltip>
    </v-row>

    <!-- DIALOG -->
    <v-row justify="center">
      <v-dialog
        v-model="dialog"
        persistent
        max-width="550"
      >
        <v-card>
          <v-card-title
            class="text-h5"
            v-html="popupTitle"
          />
          <v-card-text v-html="popupContent" />
          <v-container>
            <v-textarea
              v-if="isMajorUpdate && hasAsks"
              v-model="cancellationMessage"
            />
          </v-container>

          <v-card-actions>
            <v-spacer />
            <v-btn
              color="secondary"
              outlined
              @click="dialog = false"
            >
              {{ $t('no') }}
            </v-btn>
            <v-btn
              color="secondary"
              @click="updateAd"
            >
              {{ $t('yes') }}
            </v-btn>
          </v-card-actions>
        </v-card>
      </v-dialog>
    </v-row>
  </v-container>
</template>

<script>
import {messages_en, messages_fr, messages_eu, messages_nl} from "@translations/components/carpool/publish/AdPublish/";
import {messages_client_en, messages_client_fr, messages_client_eu, messages_client_nl} from "@clientTranslations/components/carpool/publish/AdPublish/";

import maxios from "@utils/maxios";
import debounce from "lodash/debounce";
import { merge, isEmpty, isEqual } from "lodash";
import moment from 'moment';

import SearchJourney from "@components/carpool/search/SearchJourney";
import AdPlanification from "@components/carpool/publish/AdPlanification";
import AdRoute from "@components/carpool/publish/AdRoute";
import AdSummary from "@components/carpool/publish/AdSummary";
import MMap from '@components/utilities/MMap/MMap'
import L from "leaflet";

let MessagesMergedEn = merge(messages_en, messages_client_en);
let MessagesMergedNl = merge(messages_nl, messages_client_nl);
let MessagesMergedFr = merge(messages_fr, messages_client_fr);
let MessagesMergedEu = merge(messages_eu, messages_client_eu);

export default {
  i18n: {
    messages: {
      'en': MessagesMergedEn,
      'nl': MessagesMergedNl,
      'fr': MessagesMergedFr,
      'eu': MessagesMergedEu
    },
  },
  components: {
    SearchJourney,
    AdPlanification,
    AdRoute,
    AdSummary,
    MMap
  },
  props: {
    geoSearchUrl: {
      type: String,
      default: ""
    },
    geoRouteUrl: {
      type: String,
      default: "/georoute"
    },
    user: {
      type: Object,
      default: null
    },
    defaultPriceKm: {
      type: Number,
      default: 0.06
    },
    defaultMarginDuration: {
      type: Number,
      default: null
    },
    mapProvider:{
      type: String,
      default: ""
    },
    urlTiles:{
      type: String,
      default: ""
    },
    attributionCopyright:{
      type: String,
      default: ""
    },
    communityIds: {
      type: Array,
      default: null
    },
    initOrigin: {
      type: Object,
      default: null
    },
    initDestination: {
      type: Object,
      default: null
    },
    initRegular: {
      type: Boolean,
      default: true
    },
    initDate: {
      type: String,
      default: null
    },
    initTime: {
      type: String,
      default: null
    },
    firstAd: {
      type: Boolean,
      default: false
    },
    solidaryExclusiveAd: {
      type: Boolean,
      default: false
    },
    defaultPricesRanges:{
      type: Object,
      default: null
    },
    ad: {
      type: Object,
      default: null
    },
    isUpdate: {
      type: Boolean,
      default: false
    },
    isSearchToSave: {
      type: Boolean,
      default: false
    },
    // for minor and major update popup
    hasAsks: {
      type: Boolean,
      default: false
    },
    // for major update popup only
    hasPotentialAds: {
      type: Boolean,
      default: false
    },
    defaultTimePrecision: {
      type: Number,
      default: null
    },
    participationText: {
      type: Boolean,
      default: false
    },
    ageDisplay: {
      type: Boolean,
      default: false
    },
    eventId: {
      type: Number,
      default: null
    },
    geoCompleteResultsOrder: {
      type: Array,
      default: null
    },
    geoCompletePalette: {
      type: Object,
      default: () => ({})
    },
    geoCompleteChip: {
      type: Boolean,
      default: false
    },
    seatNumber : {
      type: Number,
      default:null
    },
    defaultSeatNumber : {
      type: Number,
      default:null
    },
    contentPassenger: {
      type: Boolean,
      default: true
    },
    staggeredSchedulesAllowed: {
      type: Boolean,
      default: false
    },
    defaultDigitalClock: {
      type: Boolean,
      default: false
    },
    specificTerms: {
      type: Boolean,
      default: false
    },
    defaultRoleToPublish: {
      type: Number,
      default:null
    },
    bothRoleEnabled: {
      type: Boolean,
      default: true
    },
    communityWithFreeCarpool: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      anyRouteAsPassenger: null, // not used yet
      backSeats: false,
      baseUrl: window.location.origin,
      bbox:null,
      bike: false,
      bodyIsFullyLoaded: false,
      cancellationMessage: "",
      destination: this.initDestination,
      dialog: false,
      directionWay:[],
      disableNextButton: false,
      disconnected: false,
      distance: 0,
      driver: (this.defaultRoleToPublish && this.defaultRoleToPublish == 2) ? false : true,
      duration: 0,
      initSchedule: null,
      initWaypoints: [],
      initWaypointsCount: this.countWaypoints(),
      isValidAd: true,
      loading: false,
      loadingPrice: false,
      locale: localStorage.getItem("X-LOCALE"),
      luggage: false,
      message: null,
      oldUpdateObject: null,
      origin: this.initOrigin,
      outwardDate: this.initDate,
      outwardTime: this.initTime,
      passenger: (this.defaultRoleToPublish && this.defaultRoleToPublish == 1) ? false : true,
      pointsToMap:[],
      price: null,
      priceForbidden: false,
      pricePerKm: this.isUpdate && this.ad ? this.ad.priceKm : this.defaultPriceKm,
      regular: this.initRegular,
      regularLifeTime: null,    // not used yet
      returnDate: null,
      returnTime: null,
      returnTimeIsValid: true,
      returnTrip: null,
      route: null,
      schedules: null,
      seats : this.defaultSeatNumber,
      selectedCommunities: null,
      snackbar: {
        show: false,
        message: "",
        color: "success"
      },
      snackErrorPublish: {
        show: false,
        color: "error"
      },
      solidaryExclusive: this.solidaryExclusiveAd,
      step:1,
      strictDate: null,         // not used yet
      strictPunctual: null,     // not used yet
      strictRegular: null,      // not used yet
      userDelegated: null,      // if user delegation
      useTime: null,             // not used yet
      // specific terms
      checkboxDrivingLicence: false,
      checkboxEmployer: false,
      checkboxInssurance: false,
      freeCarpoolCommunities: [],
      freeCarpool: false,
    }
  },
  computed: {
    pricesRanges(){
      if(this.defaultPricesRanges){
        return this.defaultPricesRanges;
      }
      else{
        return {
          "mid":0.12,
          "high":0.3,
          "forbidden":0.5
        }
      }
    },
    snackErrorPublishMessage() {
      if (this.disconnected) {
        return this.disconnect();
      }
      return this.isValidAd ? this.$t("snackErrorPublish"): this.$t("snackErrorAntiFraud");
    },
    hintPricePerKm() {
      let pricePerKm = this.pricePerKm;
      if (isNaN(this.pricePerKm)) pricePerKm = 0;
      return pricePerKm.toFixed(2)+'€/km';
    },
    validWaypoints() {
      if (this.route && this.route.waypoints) {
        return this.route.waypoints.filter(function(waypoint) {
          return waypoint.visible && waypoint.address;
        });
      }
      return null;
    },
    valid() {
      // For the publish button

      // step validation
      if(this.solidaryExclusive){
        if(this.step<6) return false;
      }
      else{
        if ((this.driver && this.step != 7) || (!this.driver && this.step != 5)) return false;
      }
      // role validation
      if (this.driver === false && this.passenger === false) return false;
      // route validation
      if (this.distance<=0 || this.duration<=0 || !this.origin || !this.destination || !this.route) return false;
      // punctual date validation
      if (!this.regular && !(this.outwardDate && this.outwardTime)) return false;
      // punctual roundtrip date validation
      if (!this.regular && this.returnTrip && !(this.returnDate && this.returnTime)) return false;
      // regular date validation
      if (this.regular && !this.schedules) return false;
      // regular schedules validation
      if(this.step==2 && this.regular && (this.schedules==null || this.schedules.length==0)) return false;

      // Price to high. Forbidden to post
      if(this.priceForbidden) return false;

      // Specifics terms
      if(!this.checkboxDrivingLicence && this.driver && this.specificTerms) return false;
      if(!this.checkboxEmployer && this.driver && this.specificTerms) return false;
      if(!this.checkboxInssurance && this.driver && this.specificTerms) return false;

      // Step 2 regular schedule no return without outward
      if(this.regular && this.schedules){
        return this.isOutwardSchedulesValid();
      }

      // We are in update mode and initialization is not finished yet
      if (this.isValidUpdate && this.oldUpdateObject == null) return false;
      // update mode and there are no changes
      if (!this.isUpdated ) return false;

      // validation ok
      return true;
    },
    validNext() {

      // For the next button
      if(this.origin == null || this.destination == null) return false;
      if(!this.passenger && !this.driver) return false;
      if(!this.regular && !this.outwardDate) return false;
      if(!this.driver && this.step>4) return false;
      if(this.step>=7) return false;
      if(this.priceForbidden) return false;

      // Specifics by steps
      // Step 2 regular : you have to setup at least one schedule
      if(this.step==2 && this.regular && (this.schedules==null || this.schedules.length==0)) return false;

      // Step 2 regular schedule no return without outward
      if(this.step==2 && this.regular && this.schedules){
        return this.isOutwardSchedulesValid();
      }

      //We get here if we give at least the departure time on the 1st day
      //So now we can check on all others days, if visible and date AND at least 1 hour is not defined -> return false
      if(this.step ==2  && this.regular){
        for (var s in this.fullschedule) {
          var i = this.fullschedule[s];
          if (i.visible) {
            if ( !i.mon && !i.tue && !i.wed && !i.thu && !i.fri && !i.sat && !i.sun )  return false;
            if ( i.outwardTime == null && i.returnTime == null) return false;
          }
        }
      }

      // Step 2 punctual : you have to set the outward time
      if(this.step==2 && ((!this.regular && !(this.outwardDate && this.outwardTime)) || this.returnTimeIsValid === false)) return false;
      // Step 2 punctual, round-trip chosen : you have to set the outward date & time
      if(this.step==2 && !this.regular && this.returnTrip && !(this.returnDate && this.returnTime)) return false;

      return true;
    },
    pointEscapedPrice(){
      return this.price.replace(".",",");
    },
    colorPricePerKm(){
      if (this.pricePerKm < this.pricesRanges.mid) {
        return "success";
      } else if (this.pricePerKm >= this.pricesRanges.mid && this.pricePerKm < this.pricesRanges.high) {
        return "warning";
      } else {
        return "error";
      }
    },
    isValidUpdate () {
      return this.isUpdate && !isEmpty(this.ad);
    },
    isUpdated () {
      if (!this.isUpdate) return true;
      else return this.isValidUpdate && !isEqual(this.oldUpdateObject, this.newUpdateObject);
    },
    isMajorUpdate () {
      if (!this.isValidUpdate || isEmpty(this.oldUpdateObject)) return false;
      let newUpdateObject = this.newUpdateObject;
      return newUpdateObject.regular !== this.oldUpdateObject.regular
        || this.oldUpdateObject.driver !== newUpdateObject.driver
        || this.oldUpdateObject.returnDate !== newUpdateObject.returnDate
        || this.oldUpdateObject.returnTime !== newUpdateObject.returnTime
        || this.oldUpdateObject.outwardDate !== newUpdateObject.outwardDate
        || this.oldUpdateObject.outwardTime !== newUpdateObject.outwardTime
        || this.oldUpdateObject.passenger !== newUpdateObject.passenger
        || !isEqual(this.oldUpdateObject.origin, newUpdateObject.origin)
        || !isEqual(this.oldUpdateObject.destination, newUpdateObject.destination)
        || newUpdateObject.pricePerKm !== this.oldUpdateObject.pricePerKm
        || !isEqual(this.oldUpdateObject.waypoints, newUpdateObject.waypoints)
        || !isEqual(this.oldUpdateObject.schedules, newUpdateObject.schedules);
    },
    newUpdateObject () {
      return this.buildAdObject();
    },
    popupTitle () {
      if (this.isMajorUpdate && this.hasAsks) return this.$t('update.popup.major_update_asks.title');
      else if (this.isMajorUpdate && this.hasPotentialAds) return this.$t('update.popup.major_update_ads.title');
      else if (!this.isMajorUpdate && this.hasAsks) return this.$t('update.popup.minor_update_asks.title');
      return '';
    },
    popupContent () {
      if (this.isMajorUpdate && this.hasAsks) return this.$t('update.popup.major_update_asks.content');
      else if (this.isMajorUpdate && this.hasPotentialAds) return this.$t('update.popup.major_update_ads.content');
      else if (!this.isMajorUpdate && this.hasAsks) return this.$t('update.popup.minor_update_asks.content');
      return '';
    },
    itemsSeatNumber() {
      return [...Array(this.seatNumber+1).keys()].slice(1);
    },
    getTooltip() {
      switch (true) {
      case !this.isUpdated:
        return this.$t('stepper.buttons.unUpdated')
      case this.priceForbidden:
        return this.$t('stepper.buttons.unacceptableParticipation')
      case this.distance<=0 || this.duration<=0 || !this.origin || !this.destination || !this.route:
        return this.$t('stepper.buttons.incorrectRouteDefinition')
      case !this.regular && !(this.outwardDate && this.outwardTime):
        return this.$t('stepper.buttons.punctualOutwardDate')
      case !this.regular && this.returnTrip && !(this.returnDate && this.returnTime):
        return this.$t('stepper.buttons.punctualReturnTripOutwardDate')
      case this.regular && !this.schedules:
        return this.$t('stepper.buttons.regularSchedules')
      case this.isValidUpdate && this.oldUpdateObject == null:
        return this.$t('stepper.buttons.dataProcessing')
      case !this.checkboxDrivingLicence && this.driver && this.specificTerms:
      case !this.checkboxEmployer && this.driver && this.specificTerms:
      case !this.checkboxInssurance && this.driver && this.specificTerms:
        return this.$t('stepper.buttons.requirments')

      default: return this.$t('stepper.buttons.notValid');
      }
    },
    isMainBtnDisplayed() {
      return (
        (this.step === 7 && this.driver && this.step !== 5 && !this.solidaryExclusive)
        || (this.step === 5 && !this.driver && !this.solidaryExclusive)
        || (this.step === 6 && this.solidaryExclusive)
      )
    },
    role(){
      if(!this.showDriverRole && !this.showBothRole){
        return 2;
      }
      return this.defaultRoleToPublish;
    },
    showDriverRole(){
      return this.user.oldEnoughToDrive;
    },
    showBothRole(){
      return this.bothRoleEnabled && this.user.oldEnoughToDrive;
    }
  },
  watch: {
    price() {
      //this.pricePerKm = (this.distance>0 ? Math.round(parseFloat(this.price) / this.distance * 100)/100 : this.pricePerKm);
      this.pricePerKm = (this.distance>0 ? parseFloat(this.price) / this.distance * 100/100 : this.pricePerKm);
      (this.pricePerKm>this.pricesRanges.forbidden) ? this.priceForbidden = true : this.priceForbidden = false;
    },
    distance() {
      let price = Math.round(this.distance * this.pricePerKm * 100)/100;
      this.roundPrice(price, this.regular ? 2 : 1);
    },
    route(){
      this.buildPointsToMap();
      if(this.route.direction !== null){this.buildDirectionWay();}
      this.$refs.mmapSummary.redrawMap();
      this.$refs.mmapRoute.redrawMap();
    },
    step(){
      this.$refs.mmapSummary.redrawMap();
      this.$refs.mmapRoute.redrawMap();
    },
    outwardTime(newValue,oldValue){
      if(newValue){
        this.outwardTime = (moment(newValue).isValid()) ? moment(this.ad.outwardTime).format("HH:mm") : newValue;
      }
    },
    returnTime(newValue,oldValue){
      if(newValue){
        this.returnTime = (moment(newValue).isValid()) ? moment(this.ad.returnTime).format("HH:mm") : newValue;
      }
    },
    ad: {
      immediate: true,
      handler () {
        const self = this;
        if(this.ad){
          this.origin = this.ad.origin;
          this.outwardDate = this.ad.outwardDate;
          this.outwardTime = moment(this.ad.outwardTime).utc().format();
          this.returnDate = this.ad.returnDate;
          this.returnTime = moment(this.ad.returnTime).isValid() ? moment(this.ad.returnTime).format() : null;
          this.initWaypoints = this.ad.outwardWaypoints.filter(point => {return point.address.id !== self.initOrigin.id && point.address.id !== self.initDestination.id;});
          this.initSchedule = isEmpty(this.ad.schedule) ? {} : this.ad.schedule;
          this.seats = this.ad.seatsDriver;
          this.luggage = this.ad.luggage;
          this.smoke = this.ad.smoke;
          this.bike = this.ad.bike;
          this.backSeats = this.ad.backSeats;
          this.music = this.ad.music;
          this.message = this.ad.message;
          this.price = parseFloat(this.ad.outwardDriverPrice);
          this.pricePerKm = parseFloat(this.ad.priceKm);
          this.role = this.ad.role;
          this.driver = this.ad.role === 1 || this.ad.role === 3;
          this.passenger = this.ad.role === 2 || this.ad.role === 3;
        }
      }
    },
    freeCarpool(newValue) {
      if (newValue) {
        this.price = 0;
      }
    },
  },
  methods: {
    buildPointsToMap: function(){
      this.pointsToMap.length = 0;
      // Set the origin point with custom icon
      if(this.origin !== null && this.origin !== undefined){
        let pointOrigin = this.buildPoint(this.origin.latitude,this.origin.longitude,this.origin.displayLabel,"/images/cartography/pictos/origin.png",[36, 42],[10, 25]);
        this.pointsToMap.push(pointOrigin);
      }
      // Set all the waypoints (default icon for now)
      this.route.waypoints.forEach((waypoint, index) => {
        if(waypoint.address != null){
          let currentWaypoint = this.buildPoint(waypoint.address.latitude,waypoint.address.longitude,waypoint.address.displayLabel);
          this.pointsToMap.push(currentWaypoint);
        }
      });
      // Set the destination point with custom icon
      if(this.destination !== null && this.destination !== undefined){
        let pointDestination = this.buildPoint(this.destination.latitude,this.destination.longitude,this.destination.displayLabel,"/images/cartography/pictos/destination.png",[36, 42],[10, 25]);
        this.pointsToMap.push(pointDestination);
      }
    },
    buildDirectionWay(){
      // You need to push the entire directPoints array because the MMap component can show multiple journeys
      this.directionWay.length = 0;
      let currentDirectionWay = {
        latLngs:this.route.direction.directPoints
      };
      this.directionWay.push(currentDirectionWay);
    },
    buildPoint: function(lat,lng,title="",pictoUrl="",size=[],anchor=[]){
      let point = {
        title:title,
        latLng:L.latLng(lat, lng),
        icon: {}
      };

      if(pictoUrl!==""){
        point.icon = {
          url:pictoUrl,
          size:size,
          anchor:anchor
        }
      }

      return point;
    },
    buildUrl(route) {
      return `${this.baseUrl}/${route}`;
    },
    disconnect() {
      this.disconnected = false;
      return this.$t("snackErrorDisconnected");
    },
    isOutwardSchedulesValid() {
      let outwardSchedulesValid = true;
      this.schedules.forEach((s, index) => {
        if(!this.staggeredSchedulesAllowed && s.returnTime !== null && s.outwardTime == null){
          outwardSchedulesValid = false;
        }
      });

      return outwardSchedulesValid;
    },
    searchChanged: function(search) {
      this.passenger = search.passenger;
      this.driver = search.driver;
      this.origin = search.origin;
      this.destination = search.destination;
      this.regular = search.regular;
      this.outwardDate = search.date;
    },
    planificationChanged(planification) {
      this.outwardDate = planification.outwardDate;
      this.outwardTime = planification.outwardTime;
      this.returnDate = planification.returnDate;
      this.returnTime = planification.returnTime;
      this.schedules = planification.schedules;
      this.returnTrip = planification.returnTrip;
      this.fullschedule = planification.fullschedule;
      this.returnTimeIsValid = planification.returnTimeIsValid;
    },
    routeChanged(route) {
      if (this.isValidUpdate && this.initWaypointsCount && this.initWaypointsCount > 0) {
        this.initWaypointsCount--;
        if (this.initWaypointsCount === 0) {
          this.bodyIsFullyLoaded = true;
          this.oldUpdateObject = this.buildAdObject();
        }
      }
      this.route = route;
      this.distance = route.direction ? route.direction.distance : null;
      this.duration = route.direction ? route.direction.duration : null;
      this.selectedCommunities = route.selectedCommunities ? route.selectedCommunities : null;

      if (this.communityWithFreeCarpool) {
        this.setCommunityFreeCarpool(route.communities);
      }

      if(this.step!==1){
        this.origin = route.origin;
        this.destination = route.destination;
      }
    },
    setCommunityFreeCarpool(communities) {
      if (this.selectedCommunities) {

        for(let index in this.selectedCommunities) {
          const community = communities.find(community => community.id === this.selectedCommunities[index] && community.freeCarpool);

          if (community) {
            this.freeCarpoolCommunities.push(community);
          }
        }

        this.freeCarpool = this.freeCarpoolCommunities.length > 0;
      }
    },
    postAd() {
      let postObject = this.buildAdObject();
      this.loading = true;
      maxios.post(this.buildUrl(this.$t('route.publish')),postObject,{
        headers:{
          'content-type': 'application/json'
        }
      })
        .then(response => {
          if (response.data) {
            if (response.data.result) {
              window.location.href = this.$t("route.myAds");
            } else if (
              response.data.includes("the new Ad") ||
              response.data.includes("Too many") ||
              response.data.includes("Not enough")
            ) {
              this.snackErrorPublish.show = true;
              this.isValidAd = false;
              this.loading = false;
            } else if (response.data.includes("<login")) {
              this.disconnected = true;
              this.snackErrorPublish.show = true;
              this.loading = false;
            } else if (response.data.includes("error")) {
              this.snackErrorPublish.show = true;
              this.loading = false;
            }
          }
        })
        .catch(function (error) {
          console.log(error);
        })
        .finally(() => {
          // this.loading = false;
        });
    },
    updateAd () {
      // double check
      if (!this.isValidUpdate) {
        this.snackbar = {
          message: this.$t('update.unavailable'),
          color: "error",
          show: true
        };
        return;
      }
      this.dialog = false;
      let postObject = this.buildAdObject();
      if (this.isMajorUpdate) {
        postObject.cancellationMessage = this.cancellationMessage;
      }
      this.loading = true;
      maxios.put(this.buildUrl(this.$t('route.update', {id: this.ad.id})),postObject,{
        headers:{
          'content-type': 'application/json'
        }
      })
        .then(response => {
          if (response.data) {
            if(response.data.message == 'error'){
              this.snackErrorPublish.show = true;
              this.loading = false;
            }
            else{
              maxios.post(this.$t("route.cleanOrphans"), {})
                .then(res => {
                  window.location.href = this.$t('route.myAds');
                });
            }
          }
        })
        .catch(error => {
          let message = this.$t('update.error');
          if (error.message.includes('401')) {
            message = this.$t("snackErrorDisconnected");
          } else if (this.isSearchToSave) {
            message = this.$t('searchToSave.error');
          }
          this.snackbar = {
            message: message,
            color: "error",
            show: true
          };
          this.loading = false;
        })
        .finally(() => {
          // this.loading = false;
        });
    },
    buildAdObject () {
      let postObject = {
        regular: this.regular,
        driver: this.driver,
        passenger: this.passenger,
        origin: this.origin,
        destination: this.destination,
        solidaryExclusive: this.solidaryExclusive,
        eventId : this.eventId,
      };
      if (this.isValidUpdate) postObject.id = this.ad.id;
      if (this.userDelegated) postObject.userDelegated = this.userDelegated;
      if (this.validWaypoints) postObject.waypoints = this.validWaypoints;
      if (this.selectedCommunities) postObject.communities = this.selectedCommunities;
      if (!this.regular) {
        if (this.outwardDate) postObject.outwardDate = this.outwardDate;
        if (this.outwardTime) postObject.outwardTime = this.outwardTime;
        if (this.returnDate) postObject.returnDate = this.returnDate;
        if (this.returnTime) postObject.returnTime = this.returnTime;
      } else if (this.schedules) {
        postObject.schedules = this.schedules;
      }
      // seats proposed as a driver (not handled yet for passengers)
      if (this.driver && this.seats) postObject.seatsDriver = this.seats;
      if (this.luggage != null) postObject.luggage = this.luggage;
      if (this.bike != null) postObject.bike = this.bike;
      if (this.backSeats != null) postObject.backSeats = this.backSeats;
      // price chosen by the driver (not handled yet for passengers)

      postObject.outwardDriverPrice = 0;
      if (this.driver && this.price) {
        // for now we just handle the outward price
        postObject.outwardDriverPrice = this.solidaryExclusive ? 0 : this.price;
      }

      postObject.priceKm = 0;
      if (this.pricePerKm){
        postObject.priceKm = this.solidaryExclusive ? 0 : this.pricePerKm;
      }

      if (this.message != null) postObject.message = this.message;
      // the following parameters are not used yet but we keep them here for possible future use
      if (this.regularLifetime) postObject.regularLifetime = this.regularLifetime;
      if (this.strictDate) postObject.strictDate = this.strictDate;
      if (this.strictRegular) postObject.strictRegular = this.strictRegular;
      if (this.strictPunctual) postObject.strictPunctual = this.strictPunctual;
      if (this.useTime) postObject.useTime = this.useTime;
      if (this.anyRouteAsPassenger) postObject.anyRouteAsPassenger = this.anyRouteAsPassenger;

      return postObject;
    },
    roundPrice: debounce(function (price, frequency, doneByUser = false) {
      if (price >= 0 && frequency > 0) {
        this.loadingPrice = true;
        maxios.post(this.$t('route.roundPrice'), {
          value: price,
          frequency: frequency
        }).then(resp => {
          if(this.price !== resp.data.value) {
            this.price = resp.data.value;
            if (doneByUser === true) {
              this.snackbar = {
                message: this.$t('messageRoundedPrice'),
                color: "success",
                show: true
              };
            }
          }
        }).catch(error => {
          // if an error occurred we set the original price
          this.price = price;
        }).finally(() => {
          this.loadingPrice = false;
          if(!this.priceForbidden) this.disableNextButton = false;
        })
      }
    },1000),
    countWaypoints () {
      if (!isEmpty(this.initOrigin) && !isEmpty(this.initDestination)) {
        return 2;
      } else if (!isEmpty(this.initOrigin) || !isEmpty(this.initDestination)) {
        return 1;
      } else return 0;
    },
    scrollToElement(element = "app") {
      // this.$vuetify.goTo(0, {easing: 'easeInOutCubic'});
      document.getElementById(element).scrollIntoView();
    }
  }
};
</script>
<style scoped lang="scss">
#loading-screen {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  opacity: 0.8;
  z-index: 1000;
  background: lightgray;
}
</style>