Skip to content
Snippets Groups Projects
ProjectHeader.vue 10.9 KiB
Newer Older
Florent Lavelle's avatar
dev
Florent Lavelle committed
<template>
Florent Lavelle's avatar
Florent Lavelle committed
  <div class="project-header ui grid stackable">
Florent Lavelle's avatar
dev
Florent Lavelle committed
    <div class="row">
      <div class="three wide middle aligned column">
        <div class="margin-bottom">
          <img
            class="ui small centered image"
            alt="Thumbnail du projet"
            :src="
              project.thumbnail.includes('default')
                ? require('@/assets/img/default.png')
                : DJANGO_BASE_URL + project.thumbnail + refreshId()
            "
          >
Florent Lavelle's avatar
dev
Florent Lavelle committed
        </div>
        <div class="centered">
          <div
            class="ui basic teal label tiny-margin"
            data-tooltip="Membres"
          >
            <i
              class="user icon"
              aria-hidden="true"
            />{{ project.nb_contributors }}
          </div>
          <div
            class="ui basic teal label tiny-margin"
            data-tooltip="Signalements publiés"
          >
            <i
              class="map marker icon"
              aria-hidden="true"
            />{{ project.nb_published_features }}
          </div>
          <div
            class="ui basic teal label tiny-margin"
            data-tooltip="Commentaires"
          >
            <i
              class="comment icon"
              aria-hidden="true"
            />{{
              project.nb_published_features_comments
            }}
          </div>
Florent Lavelle's avatar
dev
Florent Lavelle committed
        </div>
      </div>

      <div class="nine wide column">
        <h1 class="ui header margin-bottom">
Florent Lavelle's avatar
dev
Florent Lavelle committed
          {{ project.title }}
        </h1>
        <div class="sub header">
          <!-- {{ project.description }} -->
          <div id="preview" />
          <textarea
            id="editor"
            v-model="project.description"
            data-preview="#preview"
            hidden
          />
Florent Lavelle's avatar
dev
Florent Lavelle committed
        </div>
      </div>

      <div class="four wide column right-column">
        <div class="ui icon right compact buttons">
          <a
            v-if="
              user &&
                permissions &&
                permissions.can_view_project &&
Florent Lavelle's avatar
dev
Florent Lavelle committed
            "
            id="subscribe-button"
            class="ui button button-hover-green tiny-margin"
            data-tooltip="Gérer mon abonnement au projet"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            data-variation="mini"
            @click="OPEN_PROJECT_MODAL('subscribe')"
          >
Florent Lavelle's avatar
Florent Lavelle committed
            <i
              class="inverted grey envelope icon"
              aria-hidden="true"
            />
Florent Lavelle's avatar
dev
Florent Lavelle committed
          </a>
          <router-link
            v-if="
              permissions &&
                permissions.can_update_project &&
Florent Lavelle's avatar
dev
Florent Lavelle committed
            "
Timothee P's avatar
Timothee P committed
            id="edit-project"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            :to="{ name: 'project_edit', params: { slug } }"
            class="ui button button-hover-orange tiny-margin"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            data-tooltip="Modifier le projet"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            data-variation="mini"
          >
Florent Lavelle's avatar
Florent Lavelle committed
            <i
              class="inverted grey pencil alternate icon"
              aria-hidden="true"
            />
Florent Lavelle's avatar
dev
Florent Lavelle committed
          </router-link>
          <a
            v-if="isProjectAdmin && isOnline"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            id="delete-button"
            class="ui button button-hover-red tiny-margin"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            data-tooltip="Supprimer le projet"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            data-variation="mini"
            @click="OPEN_PROJECT_MODAL('deleteProject')"
          >
Florent Lavelle's avatar
Florent Lavelle committed
            <i
              class="inverted grey trash icon"
              aria-hidden="true"
            />
Florent Lavelle's avatar
dev
Florent Lavelle committed
          </a>
          <div
            v-if="isProjectAdmin && !isSharedProject"
            id="share-button"
            class="ui dropdown button compact tiny-margin"
            data-tooltip="Partager le projet"
            data-position="bottom right"
            data-variation="mini"
            @click="toggleShareOptions"
          >
            <i
              class="inverted grey share icon"
              aria-hidden="true"
            />
            <div
              :class="['menu left transition', {'visible': showShareOptions}]"
              style="z-index: 9999"
            >
              <div
                v-if="project.generate_share_link"
                class="item"
                @click="copyLink"
              >
                Copier le lien de partage        
              </div>
              <div
                class="item"
                @click="copyCode"
              >
                Copier le code du Web Component
              </div>
            </div>
          </div>
Florent Lavelle's avatar
dev
Florent Lavelle committed
        </div>
        <Transition>
          <div
            v-if="confirmMsg"
            class="ui positive tiny-margin message"
          >
Florent Lavelle's avatar
dev
Florent Lavelle committed
            <span>
              Le lien a été copié dans le presse-papier
            </span>
            &nbsp;
Florent Lavelle's avatar
Florent Lavelle committed
            <i
Florent Lavelle's avatar
dev
Florent Lavelle committed
              class="close icon"
Florent Lavelle's avatar
Florent Lavelle committed
              aria-hidden="true"
              @click="confirmMsg = false"
Florent Lavelle's avatar
dev
Florent Lavelle committed
            />
          </div>
Florent Lavelle's avatar
dev
Florent Lavelle committed
      </div>

      <div
        v-if="arraysOffline.length > 0"
        class="centered"
      >
        {{ arraysOffline.length }} modification<span v-if="arraysOffline.length > 1">s</span> en attente
Florent Lavelle's avatar
dev
Florent Lavelle committed
        <button
Florent Lavelle's avatar
dev
Florent Lavelle committed
          class="ui fluid labeled teal icon button"
Florent Lavelle's avatar
Florent Lavelle committed
          @click="sendOfflineFeatures"
Florent Lavelle's avatar
dev
Florent Lavelle committed
        >
Florent Lavelle's avatar
Florent Lavelle committed
          <i
            class="upload icon"
            aria-hidden="true"
          />
Florent Lavelle's avatar
dev
Florent Lavelle committed
          Envoyer au serveur
        </button>
      </div>
    </div>
  </div>
</template>

<script>
import TextareaMarkdown from 'textarea-markdown';
Florent Lavelle's avatar
dev
Florent Lavelle committed

import { mapState, mapGetters, mapMutations } from 'vuex';

Florent Lavelle's avatar
Florent Lavelle committed
import featureAPI from '@/services/feature-api';

Florent Lavelle's avatar
dev
Florent Lavelle committed
export default {
Florent Lavelle's avatar
Florent Lavelle committed

Florent Lavelle's avatar
dev
Florent Lavelle committed
  name: 'ProjectHeader',

  props: {
    arraysOffline: {
      type: Array,
      default: () => {
        return [];
      }
    }
  },

  data() {
    return {
      slug: this.$route.params.slug,
      confirmMsg: false,
      showShareOptions: false,
Florent Lavelle's avatar
dev
Florent Lavelle committed
    };
  },

  computed: {

    ...mapState('projects', [
      'project'
    ]),
    ...mapState([
      'configuration',
    ]),
    ...mapState([
      'user',
      'user_permissions',
      'isOnline',
Florent Lavelle's avatar
dev
Florent Lavelle committed
    ]),
    ...mapGetters([
      'permissions'
    ]),

    DJANGO_BASE_URL() {
      return this.configuration.VUE_APP_DJANGO_BASE;
    },
    isProjectAdmin() {
      return this.user_permissions && this.user_permissions[this.slug] &&
        this.user_permissions[this.slug].is_project_administrator;
    },
    isSharedProject() {
      return this.$route.path.includes('projet-partage');
    },

  },

  mounted() {
    let textarea = document.querySelector('textarea');
    new TextareaMarkdown(textarea);
    window.addEventListener('mousedown', this.clickOutsideDropdown);
  },

  destroyed() {
    window.removeEventListener('mousedown', this.clickOutsideDropdown);
Florent Lavelle's avatar
dev
Florent Lavelle committed
  methods: {
    ...mapMutations('modals', [
      'OPEN_PROJECT_MODAL'
    ]),

    refreshId() {
      const crypto = window.crypto || window.msCrypto;
      var array = new Uint32Array(1);
      return '?ver=' + crypto.getRandomValues(array); // Compliant for security-sensitive use cases
Florent Lavelle's avatar
dev
Florent Lavelle committed
    },

    toggleShareOptions() {
      this.confirmMsg = false;
      this.showShareOptions = !this.showShareOptions;
    },

    clickOutsideDropdown(e) {
      // If the user click outside of the dropdown, close it
      if (!e.target.closest('#share-button')) {
        this.showShareOptions = false;
      }
    },

Florent Lavelle's avatar
dev
Florent Lavelle committed
    copyLink() {
      const sharedLink = window.location.href.replace('projet', 'projet-partage');
      navigator.clipboard.writeText(sharedLink).then(()=> {
        this.confirmMsg = true;
        setTimeout(() => {
          this.confirmMsg = false;
        }, 15000);
      }, (e) => console.error('Failed to copy link: ', e));
    },

    copyCode() {
      // Including <script> directly within template literals cause the JavaScript parser to raise syntax errors.
      // The only working workaround, but ugly, is to split and concatenate the <script> tag.
      const webComponent = `
      <!-- Pour modifier la police, ajoutez l'attribut "font" avec le nom de la police souhaitée (par exemple: font="'Roboto Condensed', Lato, 'Helvetica Neue'"). -->
      <!-- Dans le cas où la police souhaitée ne serait pas déjà disponible dans la page affichant le web component, incluez également une balise <style> pour l'importer. -->
      <style>@import url('https://fonts.googleapis.com/css?family=Roboto Condensed:400,700,400italic,700italic&subset=latin');</style>
      <scr` + `ipt src="${this.configuration.VUE_APP_DJANGO_BASE}/geocontrib/static/wc/project-preview.js"></scr` + `ipt>
      <project-preview
        domain="${this.configuration.VUE_APP_DJANGO_BASE}"
        project-slug="${this.project.slug}"
        color="${this.configuration.VUE_APP_PRIMARY_COLOR}"
        font="${this.configuration.VUE_APP_FONT_FAMILY}"
        width=""
      ></project-preview>`;

      navigator.clipboard.writeText(webComponent).then(()=> {
        this.confirmMsg = true;
        setTimeout(() => {
          this.confirmMsg = false;
        }, 15000);
      }, (e) => console.error('Failed to copy link: ', e));
Florent Lavelle's avatar
dev
Florent Lavelle committed
    },

Florent Lavelle's avatar
Florent Lavelle committed
    sendOfflineFeatures() {
      this.arraysOfflineErrors = [];

      const promises = this.arraysOffline.map((feature) => featureAPI.postOrPutFeature({
        data: feature.geojson,
        feature_id: feature.featureId,
        project__slug: feature.project,
        feature_type__slug: feature.geojson.properties.feature_type,
        method: feature.type.toUpperCase(),
      })
        .then((response) => {
Florent Lavelle's avatar
Florent Lavelle committed
          if (!response) {
            this.arraysOfflineErrors.push(feature);
          }
Florent Lavelle's avatar
Florent Lavelle committed
        })
        .catch((error) => {
          console.error(error);
          this.arraysOfflineErrors.push(feature);
        })
      );
      this.$store.commit('DISPLAY_LOADER', 'Envoi des signalements en cours.');
Florent Lavelle's avatar
Florent Lavelle committed

      Promise.all(promises).then(() => {
        this.$emit('updateLocalStorage');
        this.$emit('retrieveInfo');
        this.$store.commit('DISCARD_LOADER');
Florent Lavelle's avatar
Florent Lavelle committed
      });
    },

Florent Lavelle's avatar
dev
Florent Lavelle committed
  }

};
</script>

<style lang="less" scoped>

.project-header {
  .row .right-column {
    display: flex;
    flex-direction: column;
Florent Lavelle's avatar
dev
Florent Lavelle committed

    .ui.buttons {
      justify-content: flex-end;
        flex-grow: 0; /* avoid stretching buttons */
Florent Lavelle's avatar
dev
Florent Lavelle committed
      }
    }
  }
  .centered {
    margin: auto;
    text-align: center;
Florent Lavelle's avatar
dev
Florent Lavelle committed
  }

  .ui.dropdown > .left.menu {
    display: block;
    overflow: hidden;
    opacity: 0;
    max-height: 0;
    &.transition {
      transition: all .5s ease;
    }
    &.visible {
      opacity: 1;
      max-height: 6em;
    }
    .menu {
      margin-right: 0 !important;
      .item {
        white-space: nowrap;
      }
    }
  }

  .v-enter-active,
  .v-leave-active {
    transition: opacity .5s ease;
  }

  .v-enter-from,
  .v-leave-to {
    opacity: 0;
  }
Florent Lavelle's avatar
dev
Florent Lavelle committed
}

#preview {
  max-height: 10em;
Florent Lavelle's avatar
Florent Lavelle committed
@media  screen and (max-width: 767px) {
  .middle.aligned.column {
    text-align: center;
  }
}

Florent Lavelle's avatar
dev
Florent Lavelle committed
</style>