Feature: Übungs-Suche, fehlende Legacy-Übungen nachmigriert (110 gesamt) — SW by-v495, APP_VER 472
This commit is contained in:
parent
4c3638c17c
commit
81ee1a063e
4 changed files with 63 additions and 4 deletions
|
|
@ -1433,3 +1433,39 @@ def _migrate(conn_factory):
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
logger.info("Migration: ors_daily_total erstellt.")
|
logger.info("Migration: ors_daily_total erstellt.")
|
||||||
|
|
||||||
|
# Fehlende Legacy-JS-Übungen nachträglich einfügen
|
||||||
|
import json as _json
|
||||||
|
_legacy = [
|
||||||
|
('trick_platz_decke', 'Platz auf Decke / Matte', 'Trick', 'leicht', '5-10 min',
|
||||||
|
'Der Hund geht auf Kommando auf seine Decke oder Matte und legt sich hin.',
|
||||||
|
_json.dumps(['Decke auslegen', 'Hund mit Leckerli auf die Matte locken', 'Markerwort + Leckerli wenn er drauf steht', 'Platz-Signal einbauen', 'Distanz schrittweise erhöhen']),
|
||||||
|
'Die Matte zum positiven Ort machen, nie erzwingen.'),
|
||||||
|
('trick_suchspiel', 'Suchspiel / Nasenarbeit', 'Trick', 'leicht', '10-15 min',
|
||||||
|
'Der Hund sucht versteckte Leckerlis per Nase — mentale Auslastung pur.',
|
||||||
|
_json.dumps(['Leckerli offen zeigen', 'Hund kurz festhalten, Leckerli verstecken', 'Freigabe-Wort und suchen lassen', 'Schwierigkeit langsam steigern']),
|
||||||
|
'Nasenarbeit erschöpft mehr als körperliche Aktivität.'),
|
||||||
|
('pb_nicht_springen2', 'Nicht springen / Begrüßung', 'Problemverhalten', 'leicht', '5-10 min',
|
||||||
|
'Der Hund begrüßt Menschen ohne hochzuspringen.',
|
||||||
|
_json.dumps(['Hund ignorieren wenn er springt (Rücken zudrehen)', 'Erst belohnen wenn alle vier Pfoten am Boden', 'Gäste in die Übung einbeziehen', 'Konsequent sein']),
|
||||||
|
'Alle Familienmitglieder müssen gleich reagieren.'),
|
||||||
|
('pb_leinenfuehrigkeit', 'Leinenführigkeit — Nicht ziehen', 'Problemverhalten', 'mittel', '10-20 min',
|
||||||
|
'Der Hund läuft locker an der Leine ohne zu ziehen.',
|
||||||
|
_json.dumps(['Bei Leinenzug stehen bleiben oder Richtung wechseln', 'Lockere Leine immer belohnen', 'Kurzstrecken üben, nicht lange Spaziergänge']),
|
||||||
|
'Dauert Wochen konsequentes Üben — Geduld.'),
|
||||||
|
('pb_bellen_klaffen', 'Bellen / Kläffen', 'Problemverhalten', 'mittel', '10 min',
|
||||||
|
'Übermäßiges Bellen auf Kommando reduzieren.',
|
||||||
|
_json.dumps(['Auslöser identifizieren', '"Ruhig"-Kommando einführen wenn Pause entsteht', 'Ruhige Pausen direkt belohnen']),
|
||||||
|
'Bellen nie durch Schreien lösen — das wirkt wie Mitmachen.'),
|
||||||
|
('pb_enttriggern', 'Enttriggern / Desensibilisierung', 'Problemverhalten', 'schwer', '15-30 min',
|
||||||
|
'Den Hund langsam an angstauslösende Reize gewöhnen — für reaktive Hunde.',
|
||||||
|
_json.dumps(['Angst-Auslöser in großer Distanz zeigen', 'Bei Ruhe belohnen (kein Stress sichtbar)', 'Distanz sehr langsam verringern', 'Niemals erzwingen']),
|
||||||
|
'Immer unterhalb der Stressschwelle bleiben.'),
|
||||||
|
]
|
||||||
|
for ex in _legacy:
|
||||||
|
if not conn.execute('SELECT 1 FROM training_exercises WHERE exercise_id=?', (ex[0],)).fetchone():
|
||||||
|
conn.execute(
|
||||||
|
'INSERT INTO training_exercises (exercise_id,name,kategorie,schwierigkeit,dauer,beschreibung,schritte,tipp) VALUES (?,?,?,?,?,?,?,?)',
|
||||||
|
ex
|
||||||
|
)
|
||||||
|
logger.info(f"Migration: Übung '{ex[1]}' eingefügt.")
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '471'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '472'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
|
|
||||||
const App = (() => {
|
const App = (() => {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ window.Page_uebungen = (() => {
|
||||||
let _exercisesByTab = {}; // aus API geladen
|
let _exercisesByTab = {}; // aus API geladen
|
||||||
let _exercisesLoaded = false;
|
let _exercisesLoaded = false;
|
||||||
let _scrollTarget = null; // { exercise_id, name } — nach _renderContent() scrollen
|
let _scrollTarget = null; // { exercise_id, name } — nach _renderContent() scrollen
|
||||||
|
let _searchQuery = ''; // aktuelle Sucheingabe
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// DATEN
|
// DATEN
|
||||||
|
|
@ -566,10 +567,21 @@ window.Page_uebungen = (() => {
|
||||||
<div id="ueb-stats-banner" style="padding:var(--space-2) var(--space-4) 0"></div>
|
<div id="ueb-stats-banner" style="padding:var(--space-2) var(--space-4) 0"></div>
|
||||||
<div id="ueb-trainer" style="padding:0 var(--space-4);margin-bottom:var(--space-2)"></div>
|
<div id="ueb-trainer" style="padding:0 var(--space-4);margin-bottom:var(--space-2)"></div>
|
||||||
<div id="ueb-suggestions" style="padding:0 var(--space-4);display:flex;flex-direction:column;gap:var(--space-2);margin-bottom:var(--space-2)"></div>
|
<div id="ueb-suggestions" style="padding:0 var(--space-4);display:flex;flex-direction:column;gap:var(--space-2);margin-bottom:var(--space-2)"></div>
|
||||||
<div id="ueb-content"></div>
|
<div style="padding:var(--space-3) var(--space-4) 0" id="ueb-search-wrap">
|
||||||
|
<input type="search" id="ueb-search" placeholder="Übung suchen…"
|
||||||
|
style="width:100%;padding:var(--space-2) var(--space-3);
|
||||||
|
border:1px solid var(--c-border);border-radius:var(--radius-md);
|
||||||
|
background:var(--c-surface);color:var(--c-text);font-size:var(--text-sm);
|
||||||
|
outline:none" value="${_esc(_searchQuery)}">
|
||||||
|
</div>
|
||||||
|
<div id="ueb-content"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
_container.querySelector('#ueb-quicksetup-btn').addEventListener('click', _openQuickSetupModal);
|
_container.querySelector('#ueb-quicksetup-btn').addEventListener('click', _openQuickSetupModal);
|
||||||
|
_container.querySelector('#ueb-search')?.addEventListener('input', e => {
|
||||||
|
_searchQuery = e.target.value.trim().toLowerCase();
|
||||||
|
_renderContent();
|
||||||
|
});
|
||||||
_bindTabs();
|
_bindTabs();
|
||||||
_renderContent();
|
_renderContent();
|
||||||
_renderStatsBanner();
|
_renderStatsBanner();
|
||||||
|
|
@ -986,9 +998,20 @@ window.Page_uebungen = (() => {
|
||||||
// ÜBUNGS-CARDS
|
// ÜBUNGS-CARDS
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
function _renderUebungsList(list) {
|
function _renderUebungsList(list) {
|
||||||
|
const filtered = _searchQuery
|
||||||
|
? list.filter(u =>
|
||||||
|
u.name.toLowerCase().includes(_searchQuery) ||
|
||||||
|
(u.beschreibung || '').toLowerCase().includes(_searchQuery)
|
||||||
|
)
|
||||||
|
: list;
|
||||||
|
if (!filtered.length) {
|
||||||
|
return `<div style="padding:var(--space-8);text-align:center;color:var(--c-text-muted)">
|
||||||
|
${UI.icon('magnifying-glass')} Keine Übungen gefunden für „${_esc(_searchQuery)}"
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
return `
|
return `
|
||||||
<div style="padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-4)">
|
<div style="padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-4)">
|
||||||
${list.map((u, i) => _renderCard(u, i)).join('')}
|
${filtered.map((u, i) => _renderCard(u, i)).join('')}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v494';
|
const CACHE_VERSION = 'by-v495';
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue