Widget pour choisir ou créer un lieu fonctionnel

This commit is contained in:
Evarin 2017-04-18 02:43:05 +02:00
parent 3b030aef70
commit 7e8384f086
9 changed files with 406 additions and 35 deletions

View file

@ -482,3 +482,51 @@ div.as-results {
#map_addlieu {
height: 500px;
}
.window {
display:none;
position:fixed;
width: 100%;
height: 100%;
overflow: hidden;
top: 0;
left: 0;
z-index: 10;
&.visible {
display:block;
}
.window-bg {
background: #000;
opacity: 0.7;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.window-content {
position: relative;
margin: 0 auto;
padding: 20px;
margin-top: 50vh;
transform: translateY(-50%);
z-index: 1;
background: #eee;
max-width: 600px;
width: 90%;
max-height: 100%;
overflow: auto;
}
}
.lieu-ui {
.map {
height: 400px;
width: 100%;
}
}

View file

@ -552,3 +552,50 @@ div.as-results ul li.as-message {
#map_addlieu {
height: 500px;
}
/* line 486, ../../sass/screen.scss */
.window {
display: none;
position: fixed;
width: 100%;
height: 100%;
overflow: hidden;
top: 0;
left: 0;
z-index: 10;
}
/* line 496, ../../sass/screen.scss */
.window.visible {
display: block;
}
/* line 500, ../../sass/screen.scss */
.window .window-bg {
background: #000;
opacity: 0.7;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
/* line 511, ../../sass/screen.scss */
.window .window-content {
position: relative;
margin: 0 auto;
padding: 20px;
margin-top: 50vh;
transform: translateY(-50%);
z-index: 1;
background: #eee;
max-width: 600px;
width: 90%;
max-height: 100%;
overflow: auto;
}
/* line 528, ../../sass/screen.scss */
.lieu-ui .map {
height: 400px;
width: 100%;
}

View file

@ -0,0 +1,194 @@
function SelectLieuWidget(STATIC_ROOT, target, callback) {
var map, input, autocomplete;
var $el = $(target);
var ui_el = $el.find('.lieu-ui');
var form_el = $el.find('form');
var content_el = $el.find('.window-content');
var ui_ready = false;
var lieux_db = {};
form_el.detach();
form_el.on("submit", nouveauLieu);
function initUI(){
$.each(ui_el.children(), function(i, item){$(item).remove();});
var map_el = $("<div>", {class: "map"});
input = $("<input>",
{type:"text",
placeholder:"Chercher un établissement..."});
ui_el.append(input);
ui_el.append(map_el);
// Affiche la carte
map = L.map(map_el[0]).setView([48.8422411,2.3430553], 13);
var layer = new L.StamenTileLayer("terrain");
map.addLayer(layer);
// Autocomplete
autocomplete = new google.maps.places.Autocomplete(input[0]);
autocomplete.setTypes(["geocode", "establishment"]);
autocomplete.addListener('place_changed', handlePlaceSearch);
}
this.showWidget = function() {
$el.addClass("visible").removeClass("ajout");
if(!ui_ready)
initUI();
form_el.detach();
}
this.closeWidget = function() {
$el.removeClass("visible");
}
// Icones
function makeIcon(couleur){
return L.icon({
iconUrl: STATIC_ROOT + 'images/marker-'+couleur+'.png',
iconSize: [36, 46],
iconAnchor: [18, 45],
popupAnchor: [0, -48]
})
}
var greenIcon = makeIcon('green');
var redIcon = makeIcon('red');
var blueIcon = makeIcon('blue');
// Callback de l'autocomplete
function handlePlaceSearch() {
var place = autocomplete.getPlace();
if (!place.geometry) {
return;
}
console.log(place);
if (lieux_db.suggestion !== undefined) {
lieux_db.suggestion.marqueur.remove();
lieux_db.suggestion = undefined;
}
// Processing du lieu
var data = {};
$.each(place.address_components, function(i, obj) {
for (var j=0; j<obj.types.length; j++) {
switch(obj.types[j]) {
case "locality":
data.ville = obj.long_name;
break;
case "country":
data.pays = obj.short_name;
break;
}
}
});
data.nom = place.name;
data.coord = {lng: place.geometry.location.lng(),
lat: place.geometry.location.lat()};
data.orig_coord = data.coord;
data.fromSuggestion = true;
lieux_db.suggestion = data;
map.panTo(data.coord);
lieuSurCarte(data);
// Affichage des suggestions
$.getJSON("/api/v1/lieu/", {"format":"json",
"lat":location.lat,
"lng":location.lng}, showPropositions);
}
// Callback suggestions
function showPropositions(data) {
// TODO gérer les appels concurrents
$.each(data.objects, function(i, item) {
lieuSurCarte(item);
});
}
function lieuSurCarte(data) {
// data : des données sur un lieu, sérialisé comme par tastypie
if(data.marqueur !== undefined)
data.marqueur.remove();
var icone = blueIcon;
var fromSuggestion = false;
// Si c'est un résultat d'autocomplete
if(data.fromSuggestion === true) {
icone = redIcon;
fromSuggestion = true;
}
var marqueur = L.marker(data.coord,
{icon: icone, draggable: fromSuggestion});
data.marqueur = marqueur;
var desc = $("<div>").append($("<h3>").text(data.nom))
.append($("<p>").text(data.ville+", "+data.pays));
var activeBtn = $("<a>", {href:"javascript:void(0);"})
.prop("_lieustage_data", data)
.on("click", choixLieuStage);
if(!fromSuggestion) {
activeBtn.text("Choisir ce lieu");
} else {
var resetBtn = $("<a>", {href:"javascript:void(0);"})
.text("Réinitialiser la position")
.prop("_lieustage_data", data)
.on("click", resetOrigLieu);
desc.append($("<p>").append(resetBtn))
activeBtn.text("Créer un nouveau lieu ici");
}
desc.append($("<p>").append(activeBtn));
marqueur.bindPopup(desc[0]).addTo(map);
}
function resetOrigLieu() {
var data = this._lieustage_data;
data.marqueur.setLatLng(data.orig_coord);
map.panTo(data.orig_coord);
}
function choixLieuStage() {
var choix = this._lieustage_data;
if(!choix.fromSuggestion)
callback(choix);
else
showForm(choix);
}
function showForm(choix) {
$el.addClass("ajout");
content_el.append(form_el);
form_el.find("#id_nom").val(choix.nom);
form_el.find("#id_ville").val(choix.ville);
form_el.find("#id_pays").val(choix.pays);
form_el.find("#id_coord_0").val(choix.coord.lat);
form_el.find("#id_coord_1").val(choix.coord.lng);
}
function nouveauLieu() {
var coord = lieux_db.suggestion.marqueur.getLatLng();
form_el.find("#id_coord_0").val(coord.lat);
form_el.find("#id_coord_1").val(coord.lng);
$.post(form_el.attr("action")+"?format=json",
form_el.serialize(),
onLieuCreated);
form_el.detach();
content_el.append($("<p>").text("Envoi en cours..."));
return false;
}
function onLieuCreated(data) {
console.log(data);
if(data.success = false)
content_el.append(form_el);
else {
lieux_db.suggestion.id = data.id;
callback(lieux_db.suggestion);
}
}
}

View file

@ -1,19 +1,18 @@
{% extends "avisstage/base.html" %}
{% load staticfiles %}
{% load staticfiles avisstage_tags %}
{% block extra_head %}
<link href="{% static "jquery-autosuggest/css/autoSuggest-upshot.css" %}"
type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript"
src="{% static "jquery-autosuggest/js/jquery.autoSuggest.minified.js" %}"> </script>
<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?libraries=places&key=AIzaSyDd4innPShfHcW8KDJB833vZHZSsqt-ACw"></script>
<script type="text/javascript" src="{% static "js/leaflet.js" %}"></script>
<script type="text/javascript" src="{% static "js/leaflet-gplaces-autocomplete.js" %}"></script>
<script type="text/javascript" src="//maps.stamen.com/js/tile.stamen.js?v1.3.0"></script>
<script type="text/javascript" src="{% static "jquery-autosuggest/js/jquery.autoSuggest.minified.js" %}"> </script>
<link rel="stylesheet" type="text/css" href="{% static "css/leaflet.css" %}" />
<script type="text/javascript" src="{% static "js/tinymce/tinymce.min.js" %}"></script>
<script type="text/javascript" src="{% static "js/select_lieu.js" %}"></script>
<script type="text/javascript">
$(function() {
$(".datepicker").datepicker({ dateFormat: 'dd/mm/yy' });
} );
</script>
<script type="text/javascript" src="{% static "js/tinymce/tinymce.min.js" %}"></script>
<script type="text/javascript">
$(function() {
// Process rich text fields
var txtr = $("textarea.tinymce");
$.each(txtr, function(i, item) {
@ -31,7 +30,7 @@
language: "fr_FR",
});
// process select multiple fields
// Process select multiple fields
var slts = $("select[multiple]");
var NULL_VAL = " ";
$.each(slts, function(i, item) {
@ -66,6 +65,21 @@
$item.remove();
});
// Widget du choix du lieu
var lieu_select = new SelectLieuWidget("{{ STATIC_URL|escapejs }}",
$("#lieu_widget"), lieuChoisi);
$("#stage-addlieu").prop("_lieustage_data", "new")
.on("click", lieu_select.showWidget);
var lieu_focus;
function clickLieu() {
lieu_focus = this;
lieu_select.showWidget();
}
function lieuChoisi(lieu) {
// TODO
lieu_select.closeWidget();
}
// À l'envoi du formulaire
$("#stageform").submit(function() {
$.each(txtr, function(i, item) {
@ -109,6 +123,7 @@
{% for fform in avis_lieu_formset %}
{{ fform.lieu }}
{% endfor %}
<a href="javascript:void(0);" id="stage-addlieu">Ajouter un lieu</a>
</div>
</div>
@ -130,7 +145,9 @@
</div>
{% endfor %}
{{ avis_lieu_formset.management_form }}
<div id="avis_lieu_container">
{% for fform in avis_lieu_formset %}
<div class="avis_lieu">
<h2>Commentaire sur le lieu</h2>
{{ fform.non_field_errors }}
{% for field in fform.hidden_fields %}
@ -152,7 +169,9 @@
</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
</div>
<div id="avis_lieu_vide">
{% with avis_lieu_formset.empty_form as fform %}
<h2>Commentaire sur le lieu</h2>
@ -180,4 +199,7 @@
</div>
<input type="submit" value="Enregistrer" />
</form>
{% lieu_widget %}
{% endblock %}

View file

@ -0,0 +1,33 @@
{% load staticfiles %}
<div id="lieu_widget" class="window">
<div class="window-bg"></div>
<div class="window-content">
<a class="window-closer"></a>
<h2>Choisir un lieu</h2>
<div class="lieu-ui">
</div>
<div class="lieu-form">{% load staticfiles %}
<form action="{% url 'avisstage:lieu_ajout' %}" method="post" id="lieu_ajout">
<h1>Ajouter un lieu</h1>
{% csrf_token %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
<div class="field">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="input">
{{ field }}
{% if field.help_text %}
<p class="help_text">{{ field.help_text }}</p>
{% endif %}
</div>
</div>
{% endfor %}
<input type="submit" />
</form>
</div>
</div>
</div>

View file

View file

@ -0,0 +1,10 @@
from django import template
from avisstage.forms import LieuForm
register = template.Library()
@register.inclusion_tag('avisstage/templatetags/widget_lieu.html')
def lieu_widget():
form = LieuForm()
return {"form": form}

View file

@ -8,6 +8,7 @@ from django import forms
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from braces.views import LoginRequiredMixin
from django.http import JsonResponse
from avisstage.models import Normalien, Stage, Lieu, AvisLieu, AvisStage
from avisstage.forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm
@ -94,6 +95,22 @@ class LieuAjout(CreateView, LoginRequiredMixin):
form_class = LieuForm
template_name = 'avisstage/formulaires/lieu.html'
def form_valid(self, form):
if self.request.GET.get("format", "") == "json":
self.object = form.save()
return JsonResponse({"success": True,
"id": self.object.id})
else:
super(LieuAjout, self).form_valid(form)
def form_invalid(self, form):
if self.request.GET.get("format", "") == "json":
return JsonResponse({"success": False,
"errors": form.errors})
else:
super(LieuAjout, self).form_valid(form)
def recherche(request):
return render(request, 'avisstage/recherche.html')

View file

@ -7,8 +7,8 @@ class LatLonWidget(forms.MultiWidget):
"""
def __init__(self, attrs=None, date_format=None, time_format=None):
widgets = (forms.TextInput(attrs=attrs),
forms.TextInput(attrs=attrs))
widgets = (forms.HiddenInput(attrs=attrs),
forms.HiddenInput(attrs=attrs))
super(LatLonWidget, self).__init__(widgets, attrs)
def decompress(self, value):