Widget pour choisir ou créer un lieu fonctionnel
This commit is contained in:
parent
3b030aef70
commit
7e8384f086
9 changed files with 406 additions and 35 deletions
|
@ -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%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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%;
|
||||
}
|
||||
|
|
194
avisstage/static/js/select_lieu.js
Normal file
194
avisstage/static/js/select_lieu.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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">
|
||||
$( function() {
|
||||
$( ".datepicker" ).datepicker({ dateFormat: 'dd/mm/yy' });
|
||||
} );
|
||||
</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' });
|
||||
// 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,29 +145,33 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
{{ avis_lieu_formset.management_form }}
|
||||
{% for fform in avis_lieu_formset %}
|
||||
<h2>Commentaire sur le lieu</h2>
|
||||
{{ fform.non_field_errors }}
|
||||
{% for field in fform.hidden_fields %}
|
||||
{{ field }}
|
||||
<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 %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
{% for field in fform.visible_fields %}
|
||||
{% if not field.name == "lieu" %}
|
||||
{{ field.errors }}
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||
{% if field.help_text %}
|
||||
<p class="help_text">{{ field.help_text }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="input">
|
||||
{{ field }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for field in fform.visible_fields %}
|
||||
{% if not field.name == "lieu" %}
|
||||
{{ field.errors }}
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||
{% if field.help_text %}
|
||||
<p class="help_text">{{ field.help_text }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="input">
|
||||
{{ field }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% 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 %}
|
||||
|
|
33
avisstage/templates/avisstage/templatetags/widget_lieu.html
Normal file
33
avisstage/templates/avisstage/templatetags/widget_lieu.html
Normal 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>
|
0
avisstage/templatetags/__init__.py
Normal file
0
avisstage/templatetags/__init__.py
Normal file
10
avisstage/templatetags/avisstage_tags.py
Normal file
10
avisstage/templatetags/avisstage_tags.py
Normal 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}
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue