Newer
Older
<template>
<div class="fourteen wide column">
<script type="application/javascript" :src="baseUrl+'/resources/leaflet-control-geocoder-1.13.0/Control.Geocoder.js'"></script>
<div class="feature-list-container ui grid">
<div class="four wide column">
<h1>Signalements</h1>
</div>
<div class="twelve wide column">
<div class="ui secondary menu">
<a
@click="showMap = true"
:class="['item', { active: showMap }]"
data-tab="map"
data-tooltip="Carte"
><i class="map fitted icon"></i
></a>
<a
@click="showMap = false"
:class="['item', { active: !showMap }]"
data-tab="list"
data-tooltip="Liste"
><i class="list fitted icon"></i
></a>
<div class="item">
<h4>
{{ getFilteredFeatures().length }} signalement{{
getFilteredFeatures().length > 1 ? "s" : ""
}}
</h4>
</div>
<!-- {% if project and feature_types and
permissions|lookup:'can_create_feature' %} -->
<!-- v-if="project && feature_types && permissions" -->
<!-- //Todo: add permissions -->
<div v-if="project && feature_types" class="item right">
<div
@click="showAddFeature = !showAddFeature"
class="
ui
dropdown
top
right
pointing
compact
button button-hover-green
"
data-tooltip="Ajouter un signalement"
data-position="bottom left"
>
<i class="plus fitted icon"></i>
class="menu transition visible"
style="z-index: 9999"
>
<div class="header">Ajouter un signalement du type</div>
<div class="scrolling menu text-wrap">
<router-link
:to="{
name: 'ajouter-signalement',
v-for="(type, index) in feature_types"
:key="type.slug + index"
class="item"
>
{{ type.title }}
</router-link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<form id="form-filters" class="ui form grid" action="" method="get">
<div class="field wide four column">
<label>Type</label>
:options="form.type.choices"
:selected="form.type.selected"
:selection.sync="form.type.selected"
</div>
<div class="field wide four column">
<label>Statut</label>
<!-- //* giving an object mapped on key name -->
:options="form.status.choices"
:selected="form.status.selected.name"
:selection.sync="form.status.selected"
</div>
<div class="field wide four column">
<label>Nom</label>
<div class="ui icon input">
<i class="search icon"></i>
<div class="ui action input">
<input type="text" name="title" v-model="form.title" @input="onFilterChange()" />
<button
type="button"
class="ui teal icon button"
id="submit-search"
>
<i class="search icon"></i>
</button>
</div>
</div>
</div>
<!-- map params, updated on map move // todo : brancher sur la carte probablement -->
<input type="hidden" name="zoom" v-model="zoom" />
<input type="hidden" name="lat" v-model="lat" />
<input type="hidden" name="lng" v-model="lng" />
</form>
<div v-show="showMap" class="ui tab active map-container" data-tab="map">
<div id="map"></div>
</div>
<div v-show="!showMap" data-tab="list" class="dataTables_wrapper no-footer">
<table id="table-features" class="ui compact table">
<thead>
<tr>
<th>Statut</th>
<th>Type</th>
<th>Nom</th>
<th>Dernière modification</th>
</tr>
</thead>
<tbody>
v-for="(feature, index) in getFilteredFeatures()"
:key="index"
<td class="dt-center" :data-order="feature.properties.get_status_display">
<div v-if="feature.properties.status.value == 'archived'" data-tooltip="Archivé">
<i class="grey archive icon"></i>
</div>
<div
v-else-if="feature.properties.status.value == 'pending'"
data-tooltip="En attente de publication"
>
<i class="teal hourglass outline icon"></i>
</div>
<div
v-else-if="feature.properties.status.value == 'published'"
data-tooltip="Publié"
>
<i class="olive check icon"></i>
</div>
<div
v-else-if="feature.properties.status.value == 'draft'"
data-tooltip="Brouillon"
>
<i class="orange pencil alternate icon"></i>
</div>
</td>
<td>
<router-link
:to="{
name: 'details-type-signalement',
params: { feature_type_slug: feature.properties.feature_type.title },
>
</td>
<td>
<router-link
:to="{
name: 'details-signalement',
params: {
slug_type_signal: feature.properties.feature_type.slug,
slug_signal: feature.properties.slug || feature.id,
>{{ feature.properties.title || feature.id }}</router-link
>
</td>
<td :data-order="feature.properties.updated_on">
<!-- |date:'Ymd' -->
</td>
</td>
</tr>
<tr v-if="getFilteredFeatures().length === 0" class="odd">
<td colspan="5" class="dataTables_empty" valign="top">
Aucune donnée disponible
</td>
</tr>
</tbody>
</table>
class="dataTables_info"
id="table-features_info"
role="status"
aria-live="polite"
>
Affichage de l'élément {{ pagination.start + 1 }} à
{{ pagination.end + 1 }} sur {{ filteredFeatures.length }} éléments
</div> -->
<!-- <div
class="dataTables_paginate paging_simple_numbers"
id="table-features_paginate"
@click="toPreviousPage"
class="paginate_button previous disabled"
aria-controls="table-features"
data-dt-idx="0"
tabindex="0"
id="table-features_previous"
>Précédent</a
v-for="(page, index) in filteredFeatures.length"
:key="'page' + index"
class="paginate_button current"
aria-controls="table-features"
data-dt-idx="1"
tabindex="0"
>1</a
<!-- <span class="ellipsis">…</span> -->
<!-- <a
class="paginate_button next"
aria-controls="table-features"
data-dt-idx="7"
tabindex="0"
id="table-features_next"
@click="toNextPage"
</div>
</div>
</template>
<script>
import { mapGetters, mapState } from "vuex";
import L from "leaflet";
import { mapUtil } from "@/assets/js/map-util.js";
import SidebarLayers from "@/components/map-layers/SidebarLayers";
import Dropdown from "@/components/Dropdown.vue";
export default {
name: "Feature_list",
components: {
SidebarLayers,
data() {
return {
form: {
type: {
choices: [],
},
status: {
selected: {
name: null,
{
name: "Brouillon",
value: "draft",
},
{
name: "En attente de publication",
value: "pending",
},
{
name: "Publié",
value: "published",
},
{
name: "Archivé",
value: "archived",
},

Timothee P
committed
pagination: {
start: 0,

Timothee P
committed
},
geojsonFeatures:[],
filterStatus:null,
filterType:null,
baseUrl:this.$store.state.configuration.BASE_URL,
map:null,
zoom:null,
lat:null,
lng:null,
showAddFeature: false,
};
},
computed: {
...mapGetters(["project"]),
...mapState("feature", ["features"]),
...mapState("feature_type", ["feature_types"]),
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
baseMaps(){
return this.$store.state.map.basemaps;
},
paginatedFeatures: function () {
return this.getFilteredFeatures().slice(
this.pagination.start,
this.pagination.end
);
},
},
methods: {
onFilterStatusChange(newvalue){
this.filterStatus=null;
if(newvalue){
console.log("filter change",newvalue.value);
this.filterStatus=newvalue.value;
}
this.onFilterChange();
},
onFilterTypeChange(newvalue){
this.filterType=null;
if(newvalue){
console.log("filter change",newvalue.value);
this.filterType=newvalue.value;
}
this.onFilterChange();
},
onFilterChange(){
var features=this.getFilteredFeatures();
this.featureGroup.clearLayers();
this.featureGroup = mapUtil.addFeatures(features, {});
if(features.length>0){
mapUtil.getMap().fitBounds(this.featureGroup.getBounds())
}
},
getFilteredFeatures() {
let results = this.geojsonFeatures;
if (this.form.type.selected) {
results = results.filter(
(el) => el.properties.feature_type.title === this.form.type.selected
if (this.filterStatus) {
console.log("filter by"+this.filterStatus)
(el) => el.properties.status.value === this.filterStatus
);
}
if (this.form.title) {
results = results.filter((el) =>{
if(el.properties.title){
return el.properties.title.toLowerCase().includes(this.form.title.toLowerCase());
}
else
return el.id.toLowerCase().includes(this.form.title.toLowerCase());
}
);
}
return results;
toPreviousPage() {
if (this.pagination.start > 0) {
this.pagination.start = this.pagination.start - 15;
this.pagination.end += 15;
}
},
toNextPage() {
if (this.pagination.end < this.getFilteredFeatures().length) {
this.pagination.start += 15;
this.pagination.end = this.pagination.end - 15;
}
},
loadFeatures(features){
this.geojsonFeatures = features;
console.log(this.geojsonFeatures)
const urlParams = new URLSearchParams(window.location.search);
const featureType = urlParams.get('feature_type');
const featureStatus = urlParams.get('status');
const featureTitle = urlParams.get('title');
this.featureGroup = mapUtil.addFeatures(this.geojsonFeatures, {featureType, featureStatus, featureTitle});
// Fit the map to bound only if no initial zoom and center are defined
if ((this.lat === "" || this.lng === "" || this.zoom === "") && this.geojsonFeatures.length > 0) {
mapUtil.getMap().fitBounds(this.featureGroup.getBounds())
}
},
addGeocoders(){
let geocoder;
// Get the settings.py variable SELECTED_GEOCODER_PROVIDER. This way avoids XCC attacks
const geocoderLabel =
this.$store.state.configuration.SELECTED_GEOCODER.PROVIDER;
if (geocoderLabel) {
const LIMIT_RESULTS = 5;
if (
geocoderLabel ===
this.$store.state.configuration.GEOCODER_PROVIDERS.ADDOK
) {
geocoder = L.Control.Geocoder.addok({ limit: LIMIT_RESULTS });
} else if (
geocoderLabel ===
this.$store.state.configuration.GEOCODER_PROVIDERS.PHOTON
) {
} else if (
geocoderLabel ===
this.$store.state.configuration.GEOCODER_PROVIDERS.NOMINATIM
) {
geocoder = L.Control.Geocoder.nominatim();
}
L.Control.geocoder({
placeholder: "Chercher une adresse...",
geocoder: geocoder,
}).addTo(this.map);
}
created() {
if (!this.project) {
//this.$store.dispatch("GET_PROJECT_MESSAGES", this.$route.params.slug);
this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug);
}
},
mounted() {
this.zoom = this.$route.query.zoom||'';
this.lat = this.$route.query.lat||'';
this.lng = this.$route.query.lng||'';
var mapDefaultViewCenter = this.$store.state.configuration.DEFAULT_MAP_VIEW.center;
var mapDefaultViewZoom = this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom;
this.map=mapUtil.createMap({
zoom:this.zoom,
lat:this.lat,
lng:this.lng,
mapDefaultViewCenter,
mapDefaultViewZoom,
});
let self=this;
document.addEventListener("change-layers-order", (event) => {
// Reverse is done because the first layer in order has to be added in the map in last.
// Slice is done because reverse() changes the original array, so we make a copy first
mapUtil.updateOrder(event.detail.layers.slice().reverse());
});
// --------- End sidebar events ----------
console.log(this.$store.state.map.geojsonFeatures);
if(this.$store.state.map.geojsonFeatures){
this.loadFeatures(this.$store.state.map.geojsonFeatures);
}
else{
const url=`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`;
axios.get(url)
.then((response) => {
this.loadFeatures(response.data.features);
})
.catch((error) => {
throw error;
});
}
// Update zoom and center on each move
mapUtil.addMapEventListener("moveend", () => {
self.zoom=mapUtil.getMap().getZoom();
self.lat=mapUtil.getMap().getCenter().lat;
self.lng=mapUtil.getMap().getCenter().lng;
//$formFilters.find("input[name=zoom]").val(mapUtil.getMap().getZoom())
//$formFilters.find("input[name=lat]").val(mapUtil.getMap().getCenter().lat)
//$formFilters.find("input[name=lng]").val(mapUtil.getMap().getCenter().lng)
});
setTimeout(function () {
this.addGeocoders();
}.bind(this), 1000)
this.form.type.choices = [
//* converting Set to an Array with spread "..."
...new Set(this.features.map((el) => el.feature_type.title)), //* use Set to eliminate duplicate values
];
},
// todo : add script
};
</script>
<style>
#map {
width: 100%;
min-height: 300px;
height: calc(100vh - 300px);
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
border: 1px solid grey;
/* To not hide the filters */
z-index: 1;
}
#form-filters,
.ui.centered > .row.feature-list-container {
justify-content: flex-start;
}
.feature-list-container .ui.menu:not(.vertical) .right.item {
padding-right: 0;
}
.map-container {
width: 80vw;
transform: translateX(-50%);
margin-left: 50%;
}
@media screen and (max-width: 767px) {
#form-filters > .field.column {
width: 100% !important;
}
.map-container {
width: 100%;
}
}
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
/* datatables */
.dataTables_wrapper {
position: relative;
clear: both;
}
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate {
color: #333;
}
.dataTables_wrapper .dataTables_info {
clear: both;
float: left;
padding-top: 0.755em;
}
.dataTables_wrapper .dataTables_paginate {
float: right;
text-align: right;
padding-top: 0.25em;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current,
.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
color: #333 !important;
border: 1px solid #979797;
background-color: white;
background: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0%, #fff),
color-stop(100%, #dcdcdc)
);
background: -webkit-linear-gradient(top, #fff 0%, #dcdcdc 100%);
background: -moz-linear-gradient(top, #fff 0%, #dcdcdc 100%);
background: -ms-linear-gradient(top, #fff 0%, #dcdcdc 100%);
background: -o-linear-gradient(top, #fff 0%, #dcdcdc 100%);
background: linear-gradient(to bottom, #fff 0%, #dcdcdc 100%);
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
box-sizing: border-box;
display: inline-block;
min-width: 1.5em;
padding: 0.5em 1em;
margin-left: 2px;
text-align: center;
text-decoration: none !important;
cursor: pointer;
color: #333 !important;
border: 1px solid transparent;
border-radius: 2px;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
cursor: default;
color: #666 !important;
border: 1px solid transparent;
background: transparent;
box-shadow: none;
}
</style>