M4: PWA + Mobile-UX – Manifest/Service-Worker + Icons, Auf/Ab-Reorder, FAB, Touch-Targets + Safe-Area, Nav-Entdupidung, Heute-Highlight, Aktions-Trennung (Abhaken vs. Bearbeiten/Löschen), OIDC-Fixes + PWA-Tests
This commit is contained in:
@@ -4,21 +4,23 @@
|
||||
|
||||
{% block body %}
|
||||
<div class="card">
|
||||
<div class="row-between">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 style="margin: 0;">Mahlzeiten</h1>
|
||||
<div class="muted" style="margin-top: 0.2rem;">Planungsliste – per Drag & Drop sortieren</div>
|
||||
<h1>Mahlzeiten</h1>
|
||||
<div class="subtitle">Planungsliste – per Drag & Drop sortieren</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn" href="{{ path('mahlzeiten_neu') }}">+ Neue Mahlzeit</a>
|
||||
<a class="btn btn-link" href="{{ path('mahlzeiten_inspiration') }}">Inspiration</a>
|
||||
<a class="btn btn-link" href="{{ path('mahlzeiten_tage') }}">Tagesansicht</a>
|
||||
<a class="btn btn-link" href="{{ path('mahlzeiten_archiv') }}">Archiv</a>
|
||||
<a class="btn" href="{{ path('mahlzeiten_neu') }}">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" aria-hidden="true">
|
||||
<path d="M12 5v14M5 12h14"/>
|
||||
</svg>
|
||||
Neue Mahlzeit
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if total == 0 %}
|
||||
<p class="muted" style="margin-top: 1rem;">Noch keine Mahlzeiten in der Planung.</p>
|
||||
<p class="empty">Noch keine Mahlzeiten in der Planung.</p>
|
||||
{% else %}
|
||||
<ul class="list sortierbar">
|
||||
{% for m in mahlzeiten %}
|
||||
@@ -34,26 +36,46 @@
|
||||
<script>
|
||||
(function () {
|
||||
const liste = document.querySelector('ul.sortierbar');
|
||||
if (!liste || typeof Sortable === 'undefined') { return; }
|
||||
if (!liste) { return; }
|
||||
const url = '{{ path("mahlzeiten_reorder") }}';
|
||||
Sortable.create(liste, {
|
||||
handle: '.drag-handle',
|
||||
animation: 150,
|
||||
ghostClass: 'item-drag',
|
||||
onEnd: function () {
|
||||
const ids = Array.prototype.map.call(
|
||||
liste.querySelectorAll('li[data-id]'),
|
||||
function (li) { return Number(li.getAttribute('data-id')); }
|
||||
);
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
}).then(function (antwort) {
|
||||
if (!antwort.ok) { window.location.reload(); }
|
||||
}).catch(function () { window.location.reload(); });
|
||||
},
|
||||
|
||||
function speichern() {
|
||||
const ids = Array.prototype.map.call(
|
||||
liste.querySelectorAll('li[data-id]'),
|
||||
function (li) { return Number(li.getAttribute('data-id')); }
|
||||
);
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ids: ids }),
|
||||
}).then(function (antwort) {
|
||||
if (!antwort.ok) { window.location.reload(); }
|
||||
}).catch(function () { window.location.reload(); });
|
||||
}
|
||||
|
||||
// Auf/Ab-Buttons (mobil): Element mit Nachbar tauschen, dann speichern.
|
||||
liste.addEventListener('click', function (ereignis) {
|
||||
const knopf = ereignis.target.closest('.updown-btn');
|
||||
if (!knopf) { return; }
|
||||
const li = knopf.closest('li[data-id]');
|
||||
if (!li) { return; }
|
||||
const ziel = knopf.getAttribute('data-dir') === '1'
|
||||
? li.nextElementSibling
|
||||
: li.previousElementSibling;
|
||||
if (!ziel || !ziel.matches('li[data-id]')) { return; }
|
||||
if (knopf.getAttribute('data-dir') === '1') { li.after(ziel); } else { li.before(ziel); }
|
||||
speichern();
|
||||
});
|
||||
|
||||
// Drag & Drop (Desktop)
|
||||
if (typeof Sortable !== 'undefined') {
|
||||
Sortable.create(liste, {
|
||||
handle: '.drag-handle',
|
||||
animation: 150,
|
||||
ghostClass: 'item-drag',
|
||||
onEnd: speichern,
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user