Feature: Widerristhöhe im Hundeprofil + Ausweis-Fix (SW by-v873)
This commit is contained in:
parent
b12467286c
commit
83034c0db0
7 changed files with 58 additions and 33 deletions
|
|
@ -580,6 +580,8 @@ def _migrate(conn_factory):
|
||||||
("users", "password_reset_expires", "TEXT"),
|
("users", "password_reset_expires", "TEXT"),
|
||||||
# Fell-Typ für personalisierte Wetter-Hinweise
|
# Fell-Typ für personalisierte Wetter-Hinweise
|
||||||
("dogs", "fell_typ", "TEXT"), # kurz|mittel|lang|drahtaar|doppel|nackt
|
("dogs", "fell_typ", "TEXT"), # kurz|mittel|lang|drahtaar|doppel|nackt
|
||||||
|
# Widerristhöhe in cm (höchster Punkt Schulterblatt → Boden)
|
||||||
|
("dogs", "widerrist_cm", "REAL"),
|
||||||
# Tierarzt-Bewertungen: Durchschnitt + Anzahl am Tierarzt-Datensatz
|
# Tierarzt-Bewertungen: Durchschnitt + Anzahl am Tierarzt-Datensatz
|
||||||
("tieraerzte", "avg_rating", "REAL DEFAULT 0"),
|
("tieraerzte", "avg_rating", "REAL DEFAULT 0"),
|
||||||
("tieraerzte", "anz_bewertungen", "INTEGER DEFAULT 0"),
|
("tieraerzte", "anz_bewertungen", "INTEGER DEFAULT 0"),
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ if STAGING and os.path.isdir(PROD_MEDIA_DIR):
|
||||||
else:
|
else:
|
||||||
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
|
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
|
||||||
|
|
||||||
APP_VER = "872" # muss mit APP_VER in app.js übereinstimmen
|
APP_VER = "873" # muss mit APP_VER in app.js übereinstimmen
|
||||||
|
|
||||||
@app.get("/.well-known/assetlinks.json")
|
@app.get("/.well-known/assetlinks.json")
|
||||||
async def assetlinks():
|
async def assetlinks():
|
||||||
|
|
@ -1407,6 +1407,7 @@ async def ausweis_page(dog_id: int, request: Request):
|
||||||
<div class="meta-item"><div class="label">Geschlecht</div><div class="value">{geschlecht}</div></div>
|
<div class="meta-item"><div class="label">Geschlecht</div><div class="value">{geschlecht}</div></div>
|
||||||
<div class="meta-item"><div class="label">Gewicht</div><div class="value">{f'{dog["gewicht_kg"]} kg' if dog.get("gewicht_kg") else "–"}</div></div>
|
<div class="meta-item"><div class="label">Gewicht</div><div class="value">{f'{dog["gewicht_kg"]} kg' if dog.get("gewicht_kg") else "–"}</div></div>
|
||||||
<div class="meta-item"><div class="label">Transponder</div><div class="value">{esc(dog.get("chip_nr")) or "–"}</div></div>
|
<div class="meta-item"><div class="label">Transponder</div><div class="value">{esc(dog.get("chip_nr")) or "–"}</div></div>
|
||||||
|
{f'<div class="meta-item"><div class="label">Widerrist</div><div class="value">{dog["widerrist_cm"]} cm</div></div>' if dog.get("widerrist_cm") else ''}
|
||||||
<div class="meta-item"><div class="label">Besitzer</div><div class="value">{esc(owner["name"]) if owner else "–"}</div></div>
|
<div class="meta-item"><div class="label">Besitzer</div><div class="value">{esc(owner["name"]) if owner else "–"}</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,28 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
|
||||||
|
|
||||||
|
|
||||||
class DogCreate(BaseModel):
|
class DogCreate(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
rasse: Optional[str] = None
|
rasse: Optional[str] = None
|
||||||
geburtstag: Optional[str] = None
|
geburtstag: Optional[str] = None
|
||||||
geschlecht: Optional[str] = None
|
geschlecht: Optional[str] = None
|
||||||
gewicht_kg: Optional[float] = None
|
gewicht_kg: Optional[float] = None
|
||||||
chip_nr: Optional[str] = None
|
widerrist_cm: Optional[float] = None
|
||||||
bio: Optional[str] = None
|
chip_nr: Optional[str] = None
|
||||||
is_public: bool = False
|
bio: Optional[str] = None
|
||||||
|
is_public: bool = False
|
||||||
|
|
||||||
|
|
||||||
class DogUpdate(BaseModel):
|
class DogUpdate(BaseModel):
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
rasse: Optional[str] = None
|
rasse: Optional[str] = None
|
||||||
rasse_id: Optional[int] = None
|
rasse_id: Optional[int] = None
|
||||||
geburtstag: Optional[str] = None
|
geburtstag: Optional[str] = None
|
||||||
geschlecht: Optional[str] = None
|
geschlecht: Optional[str] = None
|
||||||
gewicht_kg: Optional[float] = None
|
gewicht_kg: Optional[float] = None
|
||||||
chip_nr: Optional[str] = None
|
widerrist_cm: Optional[float] = None
|
||||||
bio: Optional[str] = None
|
chip_nr: Optional[str] = None
|
||||||
is_public: Optional[bool] = None
|
bio: Optional[str] = None
|
||||||
|
is_public: Optional[bool] = None
|
||||||
|
|
||||||
|
|
||||||
@router.get("")
|
@router.get("")
|
||||||
|
|
@ -141,11 +143,11 @@ async def create_dog(data: DogCreate, user=Depends(get_current_user)):
|
||||||
)
|
)
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO dogs (user_id, name, rasse, geburtstag, geschlecht,
|
"""INSERT INTO dogs (user_id, name, rasse, geburtstag, geschlecht,
|
||||||
gewicht_kg, chip_nr, bio, is_public)
|
gewicht_kg, widerrist_cm, chip_nr, bio, is_public)
|
||||||
VALUES (?,?,?,?,?,?,?,?,?)""",
|
VALUES (?,?,?,?,?,?,?,?,?,?)""",
|
||||||
(user["id"], data.name, data.rasse, data.geburtstag,
|
(user["id"], data.name, data.rasse, data.geburtstag,
|
||||||
data.geschlecht, data.gewicht_kg, data.chip_nr,
|
data.geschlecht, data.gewicht_kg, data.widerrist_cm,
|
||||||
data.bio, int(data.is_public))
|
data.chip_nr, data.bio, int(data.is_public))
|
||||||
)
|
)
|
||||||
dog = conn.execute(
|
dog = conn.execute(
|
||||||
"SELECT * FROM dogs WHERE user_id=? ORDER BY id DESC LIMIT 1",
|
"SELECT * FROM dogs WHERE user_id=? ORDER BY id DESC LIMIT 1",
|
||||||
|
|
|
||||||
|
|
@ -101,9 +101,9 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||||
<link rel="stylesheet" href="/css/design-system.css?v=872">
|
<link rel="stylesheet" href="/css/design-system.css?v=873">
|
||||||
<link rel="stylesheet" href="/css/layout.css?v=872">
|
<link rel="stylesheet" href="/css/layout.css?v=873">
|
||||||
<link rel="stylesheet" href="/css/components.css?v=872">
|
<link rel="stylesheet" href="/css/components.css?v=873">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
@ -583,10 +583,10 @@
|
||||||
<div id="modal-container"></div>
|
<div id="modal-container"></div>
|
||||||
|
|
||||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||||
<script src="/js/api.js?v=872"></script>
|
<script src="/js/api.js?v=873"></script>
|
||||||
<script src="/js/ui.js?v=872"></script>
|
<script src="/js/ui.js?v=873"></script>
|
||||||
<script src="/js/app.js?v=872"></script>
|
<script src="/js/app.js?v=873"></script>
|
||||||
<script src="/js/worlds.js?v=872"></script>
|
<script src="/js/worlds.js?v=873"></script>
|
||||||
|
|
||||||
<!-- Feature-Seiten werden lazy geladen -->
|
<!-- Feature-Seiten werden lazy geladen -->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '872'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '873'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
const APP_VERSION = '1.5.1'; // ← semantische Version, wird bei make release gesetzt
|
const APP_VERSION = '1.5.1'; // ← semantische Version, wird bei make release gesetzt
|
||||||
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
const IS_STAGING = location.hostname === 'staging.banyaro.app';
|
||||||
// Cache-Bust-Parameter nach Update-Reload sofort entfernen
|
// Cache-Bust-Parameter nach Update-Reload sofort entfernen
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,12 @@ window.Page_dog_profile = (() => {
|
||||||
<div style="font-weight:500;font-size:var(--text-sm)">${dog.gewicht_kg} kg</div>
|
<div style="font-weight:500;font-size:var(--text-sm)">${dog.gewicht_kg} kg</div>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
${dog.widerrist_cm ? `
|
||||||
|
<div class="card" style="padding:var(--space-3)">
|
||||||
|
<div class="dp-info-label"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#ruler"></use></svg> Widerrist</div>
|
||||||
|
<div style="font-weight:500;font-size:var(--text-sm)">${dog.widerrist_cm} cm</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
<div class="card" style="padding:var(--space-3)">
|
<div class="card" style="padding:var(--space-3)">
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
|
||||||
margin-bottom:2px">
|
margin-bottom:2px">
|
||||||
|
|
@ -1133,6 +1139,18 @@ window.Page_dog_profile = (() => {
|
||||||
value="${dog?.gewicht_kg || ''}"
|
value="${dog?.gewicht_kg || ''}"
|
||||||
min="0.1" max="120" step="0.1" placeholder="z. B. 28.5">
|
min="0.1" max="120" step="0.1" placeholder="z. B. 28.5">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">
|
||||||
|
Widerristhöhe (cm)
|
||||||
|
${UI.help('Der Widerrist ist der höchste Punkt zwischen den Schulterblättern. Hund gerade hinstellen, senkrecht von diesem Punkt zum Boden messen. Ab 40 cm gilt der Hund in NRW als „großer Hund" (Anleinpflicht + Versicherungspflicht). In anderen Bundesländern gelten teils andere Regeln — im Zweifel bei der Gemeinde nachfragen.')}
|
||||||
|
</label>
|
||||||
|
<input class="form-control" type="number" name="widerrist_cm"
|
||||||
|
value="${dog?.widerrist_cm || ''}"
|
||||||
|
min="10" max="120" step="1" placeholder="z. B. 58">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
Chip-Nummer
|
Chip-Nummer
|
||||||
|
|
@ -1141,6 +1159,7 @@ window.Page_dog_profile = (() => {
|
||||||
<input class="form-control" type="text" name="chip_nr"
|
<input class="form-control" type="text" name="chip_nr"
|
||||||
value="${_esc(dog?.chip_nr || '')}" placeholder="15-stellig">
|
value="${_esc(dog?.chip_nr || '')}" placeholder="15-stellig">
|
||||||
</div>
|
</div>
|
||||||
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -1327,8 +1346,9 @@ window.Page_dog_profile = (() => {
|
||||||
rasse_id: fd.rasse_id ? parseInt(fd.rasse_id) : null,
|
rasse_id: fd.rasse_id ? parseInt(fd.rasse_id) : null,
|
||||||
geburtstag: fd.geburtstag || null,
|
geburtstag: fd.geburtstag || null,
|
||||||
geschlecht: fd.geschlecht || null,
|
geschlecht: fd.geschlecht || null,
|
||||||
gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null,
|
gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null,
|
||||||
chip_nr: fd.chip_nr || null,
|
widerrist_cm: fd.widerrist_cm ? parseFloat(fd.widerrist_cm) : null,
|
||||||
|
chip_nr: fd.chip_nr || null,
|
||||||
bio: fd.bio || null,
|
bio: fd.bio || null,
|
||||||
is_public: 'is_public' in fd,
|
is_public: 'is_public' in fd,
|
||||||
fell_typ: fd.fell_typ || null,
|
fell_typ: fd.fell_typ || null,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v872';
|
const CACHE_VERSION = 'by-v873';
|
||||||
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
|
||||||
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
|
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue