feat: Add bulma search
This commit is contained in:
parent
13a0da6e46
commit
965e055ef1
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<div id="navbarBasicExample" class="navbar-menu">
|
<div id="navbarBasicExample" class="navbar-menu">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<a class="navbar-item" href="{% url 'search' %}">
|
<a class="navbar-item" href="{% url 'search-bulma' %}">
|
||||||
<i class="fas fa-search"></i> {% translate 'Suchen' %}
|
<i class="fas fa-search"></i> {% translate 'Suchen' %}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
{% extends "fellchensammlung/base_bulma.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}<title>{% translate "Suche" %}</title>{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% get_current_language as LANGUAGE_CODE_CURRENT %}
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
{% include "fellchensammlung/partials/bulma-partial-map.html" %}
|
||||||
|
</div>
|
||||||
|
<form class="column" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="longitude" maxlength="200" id="longitude">
|
||||||
|
<input type="hidden" name="latitude" maxlength="200" id="latitude">
|
||||||
|
<input type="hidden" id="place_id" name="place_id">
|
||||||
|
<!--- https://docs.djangoproject.com/en/5.2/topics/forms/#reusable-form-templates -->
|
||||||
|
{{ search_form.as_p }}
|
||||||
|
<ul id="results"></ul>
|
||||||
|
<div class="container-edit-buttons">
|
||||||
|
<button class="btn" type="submit" value="search" name="search">
|
||||||
|
<i class="fas fa-search"></i> {% trans 'Suchen' %}
|
||||||
|
</button>
|
||||||
|
{% if searched %}
|
||||||
|
{% if subscribed_search %}
|
||||||
|
<button class="btn" type="submit" value="{{ subscribed_search.pk }}"
|
||||||
|
name="unsubscribe_to_search">
|
||||||
|
<i class="fas fa-bell-slash"></i> {% trans 'Suche nicht mehr abonnieren' %}
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn" type="submit" name="subscribe_to_search">
|
||||||
|
<i class="fas fa-bell"></i> {% trans 'Suche abonnieren' %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if place_not_found %}
|
||||||
|
<p class="error">
|
||||||
|
{% trans 'Ort nicht gefunden' %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% include "fellchensammlung/lists/bulma-list-adoption-notices.html" %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const locationInput = document.getElementById('id_location_string');
|
||||||
|
const resultsList = document.getElementById('results');
|
||||||
|
const placeIdInput = document.getElementById('place_id');
|
||||||
|
|
||||||
|
locationInput.addEventListener('input', async function () {
|
||||||
|
const query = locationInput.value.trim();
|
||||||
|
|
||||||
|
if (query.length < 3) {
|
||||||
|
resultsList.innerHTML = ''; // Don't search for or show results if input is less than 3 characters
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`{{ geocoding_api_url }}/?q=${encodeURIComponent(query)}&limit=5&lang={{ LANGUAGE_CODE_CURRENT }}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data && data.features) {
|
||||||
|
resultsList.innerHTML = ''; // Clear previous results
|
||||||
|
|
||||||
|
const locations = data.features.slice(0, 5); // Show only the first 5 results
|
||||||
|
|
||||||
|
locations.forEach(location => {
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
listItem.classList.add('result-item');
|
||||||
|
listItem.textContent = geojson_to_summary(location);
|
||||||
|
|
||||||
|
// Add event when user clicks on a result location
|
||||||
|
listItem.addEventListener('click', () => {
|
||||||
|
|
||||||
|
locationInput.value = geojson_to_searchable_string(location); // Set input field to selected location
|
||||||
|
resultsList.innerHTML = ''; // Clear the results after selecting a location
|
||||||
|
});
|
||||||
|
|
||||||
|
resultsList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching location data:', error);
|
||||||
|
resultsList.innerHTML = '<li class="result-item">Error fetching data. Please try again.</li>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,10 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<div class="container-cards">
|
||||||
|
{% if adoption_notices %}
|
||||||
|
{% for adoption_notice in adoption_notices %}
|
||||||
|
{% include "fellchensammlung/partials/bulma-partial-adoption-notice-minimal.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<p>{% translate "Keine Vermittlungen gefunden." %}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -0,0 +1,42 @@
|
|||||||
|
{% load custom_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="header-card-adoption-notice">
|
||||||
|
<h2 class="heading-card-adoption-notice">
|
||||||
|
<a href="{{ adoption_notice.get_absolute_url }}"> {{ adoption_notice.name }}</a>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid">
|
||||||
|
<div class="cell">
|
||||||
|
<!--- General Information --->
|
||||||
|
<div class="grid">
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b><i class="fa-solid fa-location-dot"></i></b>
|
||||||
|
{% if adoption_notice.location %}
|
||||||
|
{{ adoption_notice.location }}
|
||||||
|
{% else %}
|
||||||
|
{{ adoption_notice.location_string }}
|
||||||
|
{% endif %}</p>
|
||||||
|
</div>
|
||||||
|
<div class="cell">
|
||||||
|
{% include "fellchensammlung/partials/bulma-sex-overview.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if adoption_notice.get_photo %}
|
||||||
|
<div class="adoption-notice-img img-small">
|
||||||
|
<img src="{{ MEDIA_URL }}/{{ adoption_notice.get_photo.image }}"
|
||||||
|
alt="{{ adoption_notice.get_photo.alt_text }}">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,146 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% load custom_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<!-- add MapLibre JavaScript and CSS -->
|
||||||
|
<script src="{% settings_value "MAP_TILE_SERVER" %}/assets/lib/maplibre-gl/maplibre-gl.js"></script>
|
||||||
|
<link href="{% settings_value "MAP_TILE_SERVER" %}/assets/lib/maplibre-gl/maplibre-gl.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
<!-- add Turf see https://maplibre.org/maplibre-gl-js/docs/examples/draw-a-radius/ -->
|
||||||
|
<script src="{% static 'fellchensammlung/js/turf.min.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- add container for the map -->
|
||||||
|
<div id="map" style="width:100%;aspect-ratio:16/9"></div>
|
||||||
|
|
||||||
|
<!-- start map -->
|
||||||
|
<script>
|
||||||
|
{% if zoom_level %}
|
||||||
|
var zoom_level = {{ zoom_level }};
|
||||||
|
{% else %}
|
||||||
|
var zoom_level = 4;
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if map_center %}
|
||||||
|
var map_center = [{{ map_center.longitude | pointdecimal }}, {{ map_center.latitude | pointdecimal }}];
|
||||||
|
{% else %}
|
||||||
|
var map_center = [10.49, 50.68]; <!-- Point middle of Germany -->
|
||||||
|
zoom_level = 4; //Overwrite zoom level when no place is found
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
let map = new maplibregl.Map({
|
||||||
|
container: 'map',
|
||||||
|
style: '{% static "fellchensammlung/map/styles/colorful/style.json" %}',
|
||||||
|
center: map_center,
|
||||||
|
zoom: zoom_level
|
||||||
|
}).addControl(new maplibregl.NavigationControl());
|
||||||
|
|
||||||
|
{% for adoption_notice in adoption_notices_map %}
|
||||||
|
{% if adoption_notice.location %}
|
||||||
|
// create the popup
|
||||||
|
const popup_{{ forloop.counter }} = new maplibregl.Popup({offset: 25}).setHTML(`{% include "fellchensammlung/partials/bulma-partial-adoption-notice-minimal.html" %}`);
|
||||||
|
|
||||||
|
// create DOM element for the marker
|
||||||
|
const el_{{ forloop.counter }} = document.createElement('div');
|
||||||
|
el_{{ forloop.counter }}.id = 'marker_{{ forloop.counter }}';
|
||||||
|
el_{{ forloop.counter }}.classList.add('marker');
|
||||||
|
|
||||||
|
const location_popup_{{ forloop.counter }} = [{{ adoption_notice.location.longitude | pointdecimal }}, {{ adoption_notice.location.latitude | pointdecimal }}];
|
||||||
|
// create the marker
|
||||||
|
new maplibregl.Marker({element: el_{{ forloop.counter }}})
|
||||||
|
.setLngLat(location_popup_{{ forloop.counter }})
|
||||||
|
.setPopup(popup_{{ forloop.counter }}) // sets a popup on this marker
|
||||||
|
.addTo(map);
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for rescue_organization in rescue_organizations %}
|
||||||
|
{% if rescue_organization.location %}
|
||||||
|
// create the popup
|
||||||
|
const popup_{{ forloop.counter }} = new maplibregl.Popup({offset: 25}).setHTML(`{% include "fellchensammlung/partials/partial-rescue-organization.html" %}`);
|
||||||
|
|
||||||
|
// create DOM element for the marker
|
||||||
|
const el_{{ forloop.counter }} = document.createElement('div');
|
||||||
|
el_{{ forloop.counter }}.id = 'marker_{{ forloop.counter }}';
|
||||||
|
el_{{ forloop.counter }}.classList.add('animal-shelter-marker', 'marker');
|
||||||
|
|
||||||
|
const location_popup_{{ forloop.counter }} = [{{ rescue_organization.location.longitude | pointdecimal }}, {{ rescue_organization.location.latitude | pointdecimal }}];
|
||||||
|
// create the marker
|
||||||
|
new maplibregl.Marker({element: el_{{ forloop.counter }}})
|
||||||
|
.setLngLat(location_popup_{{ forloop.counter }})
|
||||||
|
.setPopup(popup_{{ forloop.counter }}) // sets a popup on this marker
|
||||||
|
.addTo(map);
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
map.on('load', async () => {
|
||||||
|
image = await map.loadImage('{% static "fellchensammlung/img/pin.png" %}');
|
||||||
|
map.addImage('pin', image.data);
|
||||||
|
{% for map_pin in map_pins %}
|
||||||
|
map.addSource('point_{{ forloop.counter }}', {
|
||||||
|
'type': 'geojson',
|
||||||
|
'data': {
|
||||||
|
'type': 'FeatureCollection',
|
||||||
|
'features': [
|
||||||
|
{
|
||||||
|
'type': 'Feature',
|
||||||
|
'geometry': {
|
||||||
|
'type': 'Point',
|
||||||
|
'coordinates': [{{ map_pin.location.longitude | pointdecimal }}, {{ map_pin.location.latitude | pointdecimal }}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map.addLayer({
|
||||||
|
'id': 'point_{{ forloop.counter }}',
|
||||||
|
'type': 'circle',
|
||||||
|
'source': 'point_{{ forloop.counter }}',
|
||||||
|
'paint': {
|
||||||
|
'circle-radius': 18,
|
||||||
|
'circle-color': '#ff878980'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endfor %}
|
||||||
|
});
|
||||||
|
|
||||||
|
{% if search_center %}
|
||||||
|
var search_center = [{{ search_center.longitude | pointdecimal }}, {{ search_center.latitude | pointdecimal }}];
|
||||||
|
map.on('load', () => {
|
||||||
|
const radius = {{ search_radius }}; // kilometer
|
||||||
|
const options = {
|
||||||
|
steps: 64,
|
||||||
|
units: 'kilometers'
|
||||||
|
};
|
||||||
|
const circle = turf.circle(search_center, radius, options);
|
||||||
|
|
||||||
|
// Add the circle as a GeoJSON source
|
||||||
|
map.addSource('location-radius', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: circle
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a fill layer with some transparency
|
||||||
|
map.addLayer({
|
||||||
|
id: 'location-radius',
|
||||||
|
type: 'fill',
|
||||||
|
source: 'location-radius',
|
||||||
|
paint: {
|
||||||
|
'fill-color': 'rgba(140,207,255,0.3)',
|
||||||
|
'fill-opacity': 0.5
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a line layer to draw the circle outline
|
||||||
|
map.addLayer({
|
||||||
|
id: 'location-radius-outline',
|
||||||
|
type: 'line',
|
||||||
|
source: 'location-radius',
|
||||||
|
paint: {
|
||||||
|
'line-color': '#0094ff',
|
||||||
|
'line-width': 3
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</script>
|
@ -45,6 +45,7 @@ urlpatterns = [
|
|||||||
|
|
||||||
# ex: /search/
|
# ex: /search/
|
||||||
path("suchen/", views.search, name="search"),
|
path("suchen/", views.search, name="search"),
|
||||||
|
path("bulma/suchen/", views.search_bulma, name="search-bulma"),
|
||||||
path("suchen/<slug:important_location_slug>", views.search_important_locations, name="search-by-location"),
|
path("suchen/<slug:important_location_slug>", views.search_important_locations, name="search-by-location"),
|
||||||
# ex: /map/
|
# ex: /map/
|
||||||
path("map/", views.map, name="map"),
|
path("map/", views.map, name="map"),
|
||||||
|
@ -221,7 +221,11 @@ def search_important_locations(request, important_location_slug):
|
|||||||
return render(request, 'fellchensammlung/search.html', context=context)
|
return render(request, 'fellchensammlung/search.html', context=context)
|
||||||
|
|
||||||
|
|
||||||
def search(request):
|
def search_bulma(request):
|
||||||
|
return search(request, "fellchensammlung/bulma-search.html")
|
||||||
|
|
||||||
|
|
||||||
|
def search(request, templatename="fellchensammlung/search.html"):
|
||||||
# A user just visiting the search site did not search, only upon completing the search form a user has really
|
# A user just visiting the search site did not search, only upon completing the search form a user has really
|
||||||
# searched. This will toggle the "subscribe" button
|
# searched. This will toggle the "subscribe" button
|
||||||
searched = False
|
searched = False
|
||||||
@ -260,7 +264,7 @@ def search(request):
|
|||||||
"search_radius": search.max_distance,
|
"search_radius": search.max_distance,
|
||||||
"zoom_level": zoom_level_for_radius(search.max_distance),
|
"zoom_level": zoom_level_for_radius(search.max_distance),
|
||||||
"geocoding_api_url": settings.GEOCODING_API_URL, }
|
"geocoding_api_url": settings.GEOCODING_API_URL, }
|
||||||
return render(request, 'fellchensammlung/search.html', context=context)
|
return render(request, templatename, context=context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
Loading…
x
Reference in New Issue
Block a user