Something went wrong on our end
-
Timothee P authoredTimothee P authored
Project_detail.vue 39.37 KiB
<template>
<div v-frag>
<div v-frag v-if="permissions && permissions.can_view_project && project">
<div id="message" class="fullwidth">
<div v-if="tempMessage" class="ui positive message">
<p><i class="check icon"></i> {{ tempMessage }}</p>
</div>
</div>
<div id="message_info" class="fullwidth">
<div
v-if="infoMessage"
class="ui info message"
style="text-align: left"
>
<div class="header">
<i class="info circle icon"></i> Informations
</div>
<ul class="list">
{{
infoMessage
}}
</ul>
</div>
</div>
<div class="row">
<div class="four wide middle aligned column">
<img
class="ui small spaced image"
:src="
project.thumbnail.includes('default')
? require('@/assets/img/default.png')
: DJANGO_BASE_URL + project.thumbnail + refreshId()
"
/>
<div class="ui hidden divider"></div>
<div class="ui basic teal label" data-tooltip="Membres">
<i class="user icon"></i>{{ project.nb_contributors }}
</div>
<div class="ui basic teal label" data-tooltip="Signalements publiés">
<i class="map marker icon"></i>{{ project.nb_published_features }}
</div>
<div class="ui basic teal label" data-tooltip="Commentaires">
<i class="comment icon"></i
>{{ project.nb_published_features_comments }}
</div>
</div>
<div class="ten wide column important-flex space-between">
<div>
<h1 class="ui header">
{{ project.title }}
</h1>
<div class="ui hidden divider"></div>
<div class="sub header">
{{ project.description }}
</div>
</div>
<div class="content flex flex-column-right">
<div class="flex flex-column-right">
<div class="ui icon right compact buttons flex-column-right">
<div>
<a
v-if="
user &&
permissions &&
permissions.can_view_project &&
isOffline() !== true
"
id="subscribe-button"
class="ui button button-hover-green"
data-tooltip="S'abonner au projet"
data-position="top center"
data-variation="mini"
@click="modalType = 'subscribe'"
>
<i class="inverted grey envelope icon"></i>
</a>
<router-link
v-if="
permissions &&
permissions.can_update_project &&
isOffline() !== true
"
:to="{ name: 'project_edit', params: { slug: project.slug } }"
class="ui button button-hover-orange"
data-tooltip="Modifier le projet"
data-position="top center"
data-variation="mini"
>
<i class="inverted grey pencil alternate icon"></i>
</router-link>
<a
v-if="
user_permissions &&
user_permissions[project.slug] &&
user_permissions[project.slug].is_project_administrator &&
isOffline() !== true
"
id="delete-button"
class="ui button button-hover-red"
data-tooltip="Supprimer le projet"
data-position="top center"
data-variation="mini"
@click="modalType = 'deleteProject'"
>
<i class="inverted grey trash icon"></i>
</a>
</div>
<button
v-if="user && user.is_administrator && !isSharedProject && project.generate_share_link"
class="ui teal left labeled icon button"
@click="copyLink"
>
<i class="left icon share square"></i>
Copier le lien de partage
</button>
</div>
<div v-if="confirmMsg">
<div class="ui positive tiny-margin message">
<span>
Le lien a été copié dans le presse-papier
</span>
<i class="close icon" @click="confirmMsg = ''" />
</div>
</div>
</div>
</div>
</div>
<div v-if="arraysOffline.length > 0">
{{ arraysOffline.length }} modification<span v-if="arraysOffline.length>1">s</span> en attente
<button
:disabled="isOffline()"
@click="sendOfflineFeatures()"
class="ui fluid labeled teal icon button"
>
<i class="upload icon"></i>
Envoyer au serveur
</button>
</div>
</div>
<div class="row">
<div class="seven wide column">
<h3 class="ui header">Types de signalements</h3>
<div class="ui middle aligned divided list">
<div
:class="{ active : projectInfoLoading }"
class="ui inverted dimmer"
>
<div class="ui text loader">
Récupération des types de signalements en cours...
</div>
</div>
<div
:class="{ active: featureTypeImporting }"
class="ui inverted dimmer"
>
<div class="ui text loader">
Traitement du fichier en cours ...
</div>
</div>
<div
v-for="(type, index) in feature_types"
:key="type.title + '-' + index"
class="item"
>
<div class="feature-type-container">
<router-link
:to="{
name: 'details-type-signalement',
params: { feature_type_slug: type.slug },
}"
class="feature-type-title"
>
<img
v-if="type.geom_type === 'point'"
class="list-image-type"
src="@/assets/img/marker.png"
/>
<img
v-if="type.geom_type === 'linestring'"
class="list-image-type"
src="@/assets/img/line.png"
/>
<img
v-if="type.geom_type === 'polygon'"
class="list-image-type"
src="@/assets/img/polygon.png"
/>
{{ type.title }}
</router-link>
<div class="middle aligned content">
<router-link
v-if="
project && permissions && permissions.can_create_feature
"
:to="{
name: 'ajouter-signalement',
params: { slug_type_signal: type.slug },
}"
class="
ui
compact
small
icon
right
floated
button button-hover-green
"
data-tooltip="Ajouter un signalement"
data-position="top right"
data-variation="mini"
>
<i class="ui plus icon"></i>
</router-link>
<router-link
:to="{
name: 'dupliquer-type-signalement',
params: { slug_type_signal: type.slug },
}"
v-if="
project &&
permissions &&
permissions.can_create_feature_type &&
isOffline() !== true
"
class="
ui
compact
small
icon
right
floated
button button-hover-green
"
data-tooltip="Dupliquer un type de signalement"
data-position="top right"
data-variation="mini"
>
<i class="inverted grey copy alternate icon"></i>
</router-link>
<div
v-if="isImporting(type)"
class="import-message"
>
<i class="info circle icon" />
Import en cours
</div>
<div v-else v-frag>
<a
v-if="
user_permissions &&
user_permissions[project.slug] &&
user_permissions[project.slug].is_project_administrator &&
isOffline() !== true
"
@click="toggleDeleteFeatureType(type)"
class="
ui
compact
small
icon
right
floated
button button-hover-red
"
data-tooltip="Supprimer le type de signalement"
data-position="top center"
data-variation="mini"
>
<i class="inverted grey trash alternate icon"></i>
</a>
<router-link
:to="{
name: 'editer-symbologie-signalement',
params: { slug_type_signal: type.slug },
}"
v-if="
project &&
permissions &&
permissions.can_create_feature_type &&
isOffline() != true
"
class="
ui
compact
small
icon
right
floated
button button-hover-orange
"
data-tooltip="Éditer la symbologie du type de signalement"
data-position="top center"
data-variation="mini"
>
<i class="inverted grey paint brush alternate icon"></i>
</router-link>
<router-link
:to="{
name: 'editer-type-signalement',
params: { slug_type_signal: type.slug },
}"
v-if="
project &&
type.is_editable &&
permissions &&
permissions.can_create_feature_type &&
isOffline() !== true
"
class="
ui
compact
small
icon
right
floated
button button-hover-orange
"
data-tooltip="Éditer le type de signalement"
data-position="top left"
data-variation="mini"
>
<i class="inverted grey pencil alternate icon"></i>
</router-link>
</div>
</div>
</div>
</div>
<div v-if="feature_types.length === 0">
<i> Le projet ne contient pas encore de type de signalements. </i>
</div>
</div>
<div id="nouveau-type-signalement">
<router-link
v-if="
permissions &&
permissions.can_update_project &&
isOffline() !== true
"
:to="{
name: 'ajouter-type-signalement',
params: { slug: project.slug },
}"
class="ui compact basic button button-hover-green"
>
<i class="ui plus icon"></i>Créer un nouveau type de signalement
</router-link>
</div>
<div class="nouveau-type-signalement">
<div
v-if="
permissions &&
permissions.can_update_project &&
isOffline() !== true
"
class="
ui
compact
basic
button button-hover-green
button-align-left
"
>
<i class="ui plus icon"></i>
<label class="ui" for="json_file">
<span class="label"
>Créer un nouveau type de signalement à partir d'un
GeoJSON</span
>
</label>
<input
type="file"
accept="application/json, .json, .geojson"
style="display: none"
name="json_file"
id="json_file"
@change="onFileChange"
/>
</div>
</div>
<div class="nouveau-type-signalement">
<router-link
v-if="
IDGO &&
permissions &&
permissions.can_update_project &&
isOffline() !== true
"
:to="{
name: 'catalog-import',
params: {
slug: project.slug,
feature_type_slug: 'create'
},
}"
class="ui compact basic button button-hover-green button-align-left"
>
<i class="ui plus icon"></i>Créer un nouveau type de signalement à partir du catalogue {{ CATALOG_NAME|| 'IDGO'}}
</router-link>
</div>
<div id="button-import" v-if="fileToImport.size > 0">
<button
:disabled="fileToImport.size === 0"
@click="toNewFeatureType"
class="ui fluid teal icon button"
>
<i class="upload icon"></i> Lancer l'import avec le fichier
{{ fileToImport.name }}
</button>
</div>
</div>
<div class="seven wide column">
<div
:class="{ active : mapLoading }"
class="ui inverted dimmer"
>
<div class="ui text loader">
Chargement de la carte...
</div>
</div>
<div id="map" ref="map"></div>
</div>
</div>
<div class="row">
<div class="fourteen wide column">
<div class="ui two stackable cards">
<div class="red card">
<div class="content">
<div class="center aligned header">Derniers signalements</div>
<div class="center aligned description">
<div
:class="{ active: featuresLoading }"
class="ui inverted dimmer"
>
<div class="ui text loader">
Récupération des signalements en cours...
</div>
</div>
<div class="ui relaxed list">
<div
v-for="(item, index) in features.slice(-5)"
:key="item.properties.title + index"
class="item"
>
<div class="content">
<div>
<router-link
:to="{
name: 'details-signalement',
params: {
slug: project.slug,
slug_type_signal: item.properties.feature_type.slug,
slug_signal: item.id,
},
}"
>{{ item.properties.title || item.id }}</router-link
>
</div>
<div class="description">
<i>
[{{ item.properties.created_on}}
<span
v-if="user && item.properties.creator"
>
, par {{
item.properties.creator.full_name ?
item.properties.creator.full_name :
item.properties.creator.username
}}
</span>
]
</i>
</div>
</div>
</div>
<i v-if="features.length === 0"
>Aucun signalement pour le moment.</i
>
</div>
</div>
</div>
</div>
<div class="orange card">
<div class="content">
<div class="center aligned header">Derniers commentaires</div>
<div class="center aligned description">
<div
:class="{ active: projectInfoLoading }"
class="ui inverted dimmer"
>
<div class="ui text loader">
Récupération des commentaires en cours...
</div>
</div>
<div class="ui relaxed list">
<div
v-for="(item, index) in last_comments"
:key="'comment ' + index"
class="item"
>
<div class="content">
<div>
<router-link
:to="getRouteUrl(item.related_feature.feature_url)"
>"{{ item.comment }}"</router-link
>
</div>
<div class="description">
<i
>[ {{ item.created_on
}}<span v-if="user && item.display_author"
>, par {{ item.display_author }}
</span>
]</i
>
</div>
</div>
</div>
<i v-if="!last_comments || last_comments.length === 0"
>Aucun commentaire pour le moment.</i
>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="fourteen wide column">
<div class="ui grey segment">
<h3 class="ui header">Paramètres du projet</h3>
<div class="ui five stackable cards">
<div class="card">
<div class="center aligned content">
<h4 class="ui center aligned icon header">
<i class="disabled grey archive icon"></i>
<div class="content">Délai avant archivage automatique</div>
</h4>
</div>
<div class="center aligned extra content">
{{ project.archive_feature }} jours
</div>
</div>
<div class="card">
<div class="content">
<h4 class="ui center aligned icon header">
<i class="disabled grey trash alternate icon"></i>
<div class="content">
Délai avant suppression automatique
</div>
</h4>
</div>
<div class="center aligned extra content">
{{ project.delete_feature }} jours
</div>
</div>
<div class="card">
<div class="content">
<h4 class="ui center aligned icon header">
<i class="disabled grey eye icon"></i>
<div class="content">
Visibilité des signalements publiés
</div>
</h4>
</div>
<div class="center aligned extra content">
{{ project.access_level_pub_feature }}
</div>
</div>
<div class="card">
<div class="content">
<h4 class="ui center aligned icon header">
<i class="disabled grey eye icon"></i>
<div class="content">
Visibilité des signalements archivés
</div>
</h4>
</div>
<div class="center aligned extra content">
{{ project.access_level_arch_feature }}
</div>
</div>
<div class="card">
<div class="content">
<h4 class="ui center aligned icon header">
<i class="disabled grey cogs icon"></i>
<div class="content">Modération</div>
</h4>
</div>
<div class="center aligned extra content">
{{ project.moderation ? "Oui" : "Non" }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span v-else>
<i class="icon exclamation triangle"></i>
<span
>Vous ne disposez pas des droits nécessaires pour consulter ce
projet.</span
>
</span>
<div
v-if="modalType"
class="ui dimmer modals page transition visible active"
style="display: flex !important"
>
<div
:class="[
'ui mini modal subscription',
{ 'transition visible active': modalType },
]"
>
<i @click="modalType = false" class="close icon"></i>
<div class="ui icon header">
<i :class="[modalType === 'subscribe' ? 'envelope' : 'trash', 'icon']"></i>
{{
modalType === 'subscribe' ? 'Notifications' : 'Suppression'
}} du {{
modalType === 'deleteFeatureType' ? 'type de signalement ' + featureTypeToDelete.title : 'projet'
}}
</div>
<div class="content">
<div v-if="modalType !== 'subscribe'" >
<p class="centered-text">
Confirmez vous la suppression du {{ modalType === 'deleteProject' ? 'projet, ainsi que les types de signalements' : 'type de signalement'}} et tous les signalements associés ?
</p>
<p class="centered-text alert">
Attention cette action est irreversible !
</p>
</div>
<button
@click="handleModalClick"
:class="['ui compact fluid button', modalType === 'subscribe' && !is_suscriber ? 'green' : 'red']"
>
<span v-if="modalType === 'subscribe'">
{{
is_suscriber
? "Se désabonner de ce projet"
: "S'abonner à ce projet"
}}
</span>
<span v-else>
Supprimer le
{{
modalType === 'deleteProject'
? 'projet'
: 'type de signalement'
}}
</span>
</button>
</div>
</div>
</div>
<div
:class="isFileSizeModalOpen ? 'active' : ''"
class="ui dimmer"
>
<div
:class="isFileSizeModalOpen ? 'active' : ''"
class="ui modal tiny"
style="top: 20%"
>
<div class="header">
Fichier trop grand!
</div>
<div class="content">
<p>
Impossible de créer un type de signalement à partir d'un fichier GeoJSON
de plus de 10Mo (celui importé fait {{ fileSize }} Mo).
</p>
</div>
<div class="actions">
<div
class="ui button teal"
@click="closeFileSizeModal"
>
Fermer
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import frag from "vue-frag";
import { mapUtil } from "@/assets/js/map-util.js";
import { mapGetters, mapState, mapActions, mapMutations } from "vuex";
import projectAPI from "@/services/project-api";
import featureTypeAPI from "@/services/featureType-api";
import featureAPI from "@/services/feature-api";
import axios from "@/axios-client.js";
import { fileConvertSizeToMo } from '@/assets/js/utils';
export default {
name: "Project_details",
props: ["message"],
directives: {
frag,
},
filters: {
setDate(value) {
const date = new Date(value);
const d = date.toLocaleDateString("fr", {
year: "2-digit",
month: "numeric",
day: "numeric",
});
return d;
},
},
data() {
return {
infoMessage: "",
importMessage: null,
arraysOffline: [],
arraysOfflineErrors: [],
confirmMsg: false,
geojsonImport: [],
fileToImport: { name: "", size: 0 },
slug: this.$route.params.slug,
modalType: false,
is_suscriber: false,
tempMessage: null,
projectInfoLoading: true,
featureTypeImporting: false,
featureTypeToDelete: null,
featuresLoading: true,
isFileSizeModalOpen: false,
// mapFeatures: null,
mapLoading: true,
};
},
computed: {
...mapGetters([
'permissions'
]),
...mapGetters('projects', [
'project'
]),
...mapState([
'configuration',
]),
...mapState('feature_type', [
'feature_types',
'importFeatureTypeData'
]),
...mapState('feature', [
'features'
]),
...mapState([
'last_comments',
'user',
'user_permissions',
'reloadIntervalId',
]),
...mapState('map', [
'map'
]),
DJANGO_BASE_URL() {
return this.configuration.VUE_APP_DJANGO_BASE;
},
API_BASE_URL() {
return this.configuration.VUE_APP_DJANGO_API_BASE;
},
CATALOG_NAME() {
return this.configuration.VUE_APP_CATALOG_NAME;
},
IDGO() {
return this.$store.state.configuration.VUE_APP_IDGO;
},
fileSize() {
return fileConvertSizeToMo(this.fileToImport.size);
},
isSharedProject() {
return this.$route.path.includes('projet-partage');
}
},
watch: {
feature_types: {
deep: true,
handler(newValue, oldValue) {
if (newValue && newValue !== oldValue) {
this.GET_IMPORTS({
project_slug: this.$route.params.slug
});
}
}
},
importFeatureTypeData: {
deep: true,
handler(newValue) {
if (newValue && newValue.some(el => el.status === 'pending') && !this.reloadIntervalId) {
this.SET_RELOAD_INTERVAL_ID(setInterval(() => {
this.GET_IMPORTS({
project_slug: this.$route.params.slug
});
}, this.$store.state.configuration.VUE_APP_RELOAD_INTERVAL));
} else if (newValue && !newValue.some(el => el.status === 'pending') && this.reloadIntervalId) {
this.GET_PROJECT_FEATURE_TYPES(this.project.slug);
this.CLEAR_RELOAD_INTERVAL_ID();
}
}
},
},
created() {
if (this.user) {
projectAPI
.getProjectSubscription({
baseUrl: this.$store.state.configuration.VUE_APP_DJANGO_API_BASE,
projectSlug: this.$route.params.slug
})
.then((data) => (this.is_suscriber = data.is_suscriber));
}
this.$store.commit("feature/SET_FEATURES", []); //* empty features remaining in case they were in geojson format and will be fetch after map initialization anyway
this.$store.commit("feature_type/SET_FEATURE_TYPES", []); //* empty feature_types remaining from previous project
},
mounted() {
this.retrieveProjectInfo();
if (this.message) {
this.tempMessage = this.message;
document
.getElementById("message")
.scrollIntoView({ block: "end", inline: "nearest" });
setTimeout(() => (this.tempMessage = null), 5000); //* hide message after 5 seconds
}
},
destroyed() {
this.CLEAR_RELOAD_INTERVAL_ID();
},
methods: {
...mapMutations([
'SET_RELOAD_INTERVAL_ID',
'CLEAR_RELOAD_INTERVAL_ID',
'DISPLAY_MESSAGE',
]),
...mapActions('projects', [
'GET_PROJECT_INFO',
]),
...mapActions('map', [
'INITIATE_MAP'
]),
...mapActions('feature_type', [
'GET_IMPORTS'
]),
...mapActions('feature', [
'GET_PROJECT_FEATURES'
]),
...mapActions('feature_type', [
'GET_PROJECT_FEATURE_TYPES'
]),
refreshId() {
return "?ver=" + Math.random();
},
getRouteUrl(url) {
if (this.isSharedProject) {
url = url.replace("projet", "projet-partage")
}
return url.replace(this.$store.state.configuration.BASE_URL, ""); //* remove duplicate /geocontrib
},
isOffline() {
return navigator.onLine === false;
},
isImporting(type) {
if (this.importFeatureTypeData) {
const singleImportData = this.importFeatureTypeData.find(el => el.feature_type_title === type.slug);
return singleImportData && singleImportData.status === 'pending'
}
return false;
},
copyLink() {
const sharedLink = window.location.href.replace("projet", "projet-partage");
navigator.clipboard.writeText(sharedLink).then(()=> {
console.log("success")
this.confirmMsg = true;
}, () => {
console.log("failed")
}
)
},
retrieveProjectInfo() {
this.GET_PROJECT_INFO(this.slug)
.then(() => {
this.projectInfoLoading = false;
setTimeout(() => {
let map = mapUtil.getMap();
if (map) map.remove();
this.initMap();
}, 1000);
})
.catch((err) => {
console.error(err)
this.projectInfoLoading = false;
});
},
checkForOfflineFeature() {
let arraysOffline = [];
let localStorageArray = localStorage.getItem("geocontrib_offline");
if (localStorageArray) {
arraysOffline = JSON.parse(localStorageArray);
this.arraysOffline = arraysOffline.filter(
(x) => x.project === this.project.slug
);
}
},
sendOfflineFeatures() {
var promises = [];
let self = this;
this.arraysOfflineErrors = [];
this.arraysOffline.forEach((feature) => {
if (feature.type === "post") {
promises.push(
axios
.post(`${this.API_BASE_URL}features/`, feature.geojson)
.then((response) => {
if (response.status === 201 && response.data) {
return "OK"
}
else{
self.arraysOfflineErrors.push(feature);
}
})
.catch((error) => {
console.error(error);
self.arraysOfflineErrors.push(feature);
})
);
} else if (feature.type === "put") {
promises.push(
axios
.put(
`${this.API_BASE_URL}features/${feature.featureId}`,
feature.geojson
)
.then((response) => {
if (response.status === 200 && response.data) {
return "OK"
}
else{
self.arraysOfflineErrors.push(feature);
}
})
.catch((error) => {
console.error(error);
self.arraysOfflineErrors.push(feature);
})
);
}
});
Promise.all(promises).then(() => {
this.updateLocalStorage();
window.location.reload();
});
},
updateLocalStorage() {
let arraysOffline = [];
let localStorageArray = localStorage.getItem("geocontrib_offline");
if (localStorageArray) {
arraysOffline = JSON.parse(localStorageArray);
}
let arraysOfflineOtherProject = arraysOffline.filter(
(x) => x.project !== this.project.slug
);
this.arraysOffline = [];
arraysOffline = arraysOfflineOtherProject.concat(this.arraysOfflineErrors);
localStorage.setItem("geocontrib_offline", JSON.stringify(arraysOffline));
},
toNewFeatureType() {
this.featureTypeImporting = true;
this.$router.push({
name: "ajouter-type-signalement",
params: {
geojson: this.geojsonImport,
fileToImport: this.fileToImport,
},
});
this.featureTypeImporting = false;
},
onFileChange(e) {
this.featureTypeImporting = true;
var files = e.target.files || e.dataTransfer.files;
if (!files.length) return;
this.fileToImport = files[0];
// TODO : VALIDATION IF FILE IS JSON
if (parseFloat(fileConvertSizeToMo(this.fileToImport.size)) > 10) {
this.isFileSizeModalOpen = true;
} else if (this.fileToImport.size > 0) {
const fr = new FileReader();
try {
fr.onload = (e) => {
this.geojsonImport = JSON.parse(e.target.result);
this.featureTypeImporting = false;
};
fr.readAsText(this.fileToImport);
//* stock filename to import features afterward
this.$store.commit(
"feature_type/SET_FILE_TO_IMPORT",
this.fileToImport
);
} catch (err) {
console.error(err);
this.featureTypeImporting = false
}
} else {
this.featureTypeImporting = false;
}
},
closeFileSizeModal() {
this.fileToImport = { name: "", size: 0 };
this.featureTypeImporting = false;
this.isFileSizeModalOpen = false;
},
subscribeProject() {
projectAPI
.subscribeProject({
baseUrl: this.$store.state.configuration.VUE_APP_DJANGO_API_BASE,
suscribe: !this.is_suscriber,
projectSlug: this.$route.params.slug,
})
.then((data) => {
this.is_suscriber = data.is_suscriber;
this.modalType = false;
if (this.is_suscriber)
this.infoMessage =
"Vous êtes maintenant abonné aux notifications de ce projet.";
else
this.infoMessage =
"Vous ne recevrez plus les notifications de ce projet.";
setTimeout(() => (this.infoMessage = ""), 3000);
});
},
deleteProject() {
projectAPI.deleteProject(this.API_BASE_URL, this.project.slug)
.then((response) => {
if (response === 'success') {
this.$router.push('/');
this.DISPLAY_MESSAGE(`Le projet ${this.project.title} a bien été supprimé.`)
} else {
this.DISPLAY_MESSAGE(`Une erreur est survenu lors de la suppression du projet ${this.project.title}.`)
}
})
},
deleteFeatureType() {
featureTypeAPI.deleteFeatureType(this.featureTypeToDelete.slug)
.then((response) => {
this.modalType = false;
if (response === 'success') {
this.GET_PROJECT();
this.retrieveProjectInfo();
this.DISPLAY_MESSAGE(`Le type de signalement ${this.featureTypeToDelete.title} a bien été supprimé.`)
} else {
this.DISPLAY_MESSAGE(`Une erreur est survenu lors de la suppression du type de signalement ${this.featureTypeToDelete.title}.`)
}
this.featureTypeToDelete = null;
})
},
handleModalClick() {
switch (this.modalType) {
case 'subscribe':
this.subscribeProject();
break;
case 'deleteProject':
this.deleteProject();
break;
case 'deleteFeatureType':
this.deleteFeatureType();
break;
}
},
toggleDeleteFeatureType(featureType) {
this.featureTypeToDelete = featureType;
this.modalType = 'deleteFeatureType';
},
async initMap() {
if (this.project && this.permissions.can_view_project) {
await this.INITIATE_MAP(this.$refs.map);
this.checkForOfflineFeature();
let project_id = this.$route.params.slug.split("-")[0];
const mvtUrl = `${this.API_BASE_URL}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
mapUtil.addVectorTileLayer(
mvtUrl,
this.$route.params.slug,
this.$store.state.feature_type.feature_types
);
this.arraysOffline.forEach((x) => (x.geojson.properties.color = "red"));
const features = this.arraysOffline.map((x) => x.geojson);
mapUtil.addFeatures(
features,
{},
true,
this.$store.state.feature_type.feature_types
);
this.mapLoading = false;
this.GET_PROJECT_FEATURES({
project_slug: this.slug,
ordering: '-created_on',
limit: null,
geojson: true
})
.then(() => {
this.featuresLoading = false;
})
.catch((err) => {
console.error(err)
this.featuresLoading = false;
});
featureAPI
.getFeaturesBbox(this.project.slug)
.then((bbox) => {
if (bbox) {
mapUtil.getMap().fitBounds(bbox, { padding: [25, 25] });
}
});
}
},
},
};
</script>
<style>
@import "../../assets/resources/semantic-ui-2.4.2/semantic.min.css";
#map {
width: 100%;
height: 100%;
min-height: 250px;
}
/* // ! missing style in semantic.min.css, je ne comprends pas comment... */
.ui.right.floated.button {
float: right;
}
</style>
<style scoped>
.list-image-type {
margin-right: 5px;
height: 25px;
vertical-align: bottom;
}
.feature-type-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.feature-type-container > .middle.aligned.content {
width: 50%;
}
.feature-type-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 1.5em;
}
.nouveau-type-signalement {
margin-top: 1em;
}
.nouveau-type-signalement .label{
cursor: pointer;
}
#button-import {
margin-top: 0.5em;
}
.fullwidth {
width: 100%;
}
.button-align-left {
display: flex;
align-items: center;
text-align: left;
width: fit-content;
}
.space-between {
justify-content: space-between;
}
.flex-column-right {
flex-direction: column !important;
align-items: flex-end;
}
.import-message {
width: fit-content;
line-height: 2em;
color: teal;
}
</style>
<style scoped>
.ui.button, .ui.button .button, .tiny-margin {
margin: 0.1rem 0 0.1rem 0.1rem !important;
}
.alert {
color: red;
}
.centered-text {
text-align: center;
}
</style>