Feature: Tierschutz-Check, KI-Züchter-Features, Export, SEO-Update
Tierschutz-System (immer aktiv, nicht abschaltbar): - welfare_check.py: regelbasierte Prüfung IK, Alter, Deckpause, Wurfanzahl, Genetik - Grün/Gelb/Rot-Modal bei Wurf anlegen + Probeverpaarung - Bei kritischem Befund + "Trotzdem fortfahren" → automatische Admin-Mail - Tierschutz-Check nie durch Nutzer deaktivierbar KI-Züchter-Features (pro User an/abschaltbar außer Tierschutz): - routes/zucht_ki.py: 5 Endpunkte — Wurfankündigung, Genetik-Erklärung, Paarungsanalyse, Hund-Beschreibung, Jahresbericht - Toggles in Einstellungen (ki_zucht_* Felder) - KI-Buttons in litters.js + zuchthunde.js KI-Routing: Privilegierte Rollen (Admin, Züchter, Moderator, Manager) nutzen Claude Sonnet primär, lokales LLM als Fallback Datenexport: routes/breeder_export.py — ZIP mit HTML-Dossier + ODS (odfpy hinzugefügt in requirements.txt) Admin-Profil: POST /admin/breeder/create-profile für Schnellprofil ohne Antragsprozess; Admin-Rolle bleibt erhalten Wurfformular: Dropdown aus Zuchtkartei für Vater/Mutter mit Auto-Fill; litters.vater_id + mutter_id als FK auf zucht_hunde Probeverpaarung: heart-fill Icon + Welfare-Block im Ergebnis Landing Page: Züchter-Section + Feature-Gruppe, Meta-Tags, JSON-LD, keywords, softwareVersion 2.1 SEO: llms.txt vollständig überarbeitet, robots.txt Züchter-Pfade, sitemap.xml um Wurfbörse + Züchter-Profile erweitert SW by-v474, APP_VER 451
This commit is contained in:
parent
91340be5a3
commit
c8ae514c01
20 changed files with 2129 additions and 200 deletions
|
|
@ -177,6 +177,9 @@
|
|||
<rect width="256" height="256" fill="none"/>
|
||||
<path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM152,160H136v16a8,8,0,0,1-16,0V160H104a8,8,0,0,1,0-16h16V128a8,8,0,0,1,16,0v16h16a8,8,0,0,1,0,16ZM48,80V48H72v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80Z"/>
|
||||
</symbol>
|
||||
<symbol id="heart-fill" viewBox="0 0 256 256">
|
||||
<path d="M240,102c0,70-103.79,126.66-108.21,129a8,8,0,0,1-7.58,0C119.79,228.66,16,172,16,102A62.07,62.07,0,0,1,78,40c20.65,0,38.73,8.88,50,23.89C139.27,48.88,157.35,40,178,40A62.07,62.07,0,0,1,240,102Z"/>
|
||||
</symbol>
|
||||
<symbol id="tree-structure" viewBox="0 0 256 256">
|
||||
<path d="M144,96V80H128a8,8,0,0,0-8,8v80a8,8,0,0,0,8,8h16V160a16,16,0,0,1,16-16h48a16,16,0,0,1,16,16v48a16,16,0,0,1-16,16H160a16,16,0,0,1-16-16V192H128a24,24,0,0,1-24-24V136H72v8a16,16,0,0,1-16,16H24A16,16,0,0,1,8,144V112A16,16,0,0,1,24,96H56a16,16,0,0,1,16,16v8h32V88a24,24,0,0,1,24-24h16V48a16,16,0,0,1,16-16h48a16,16,0,0,1,16,16V96a16,16,0,0,1-16,16H160A16,16,0,0,1,144,96Z"/>
|
||||
</symbol>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |
|
|
@ -615,6 +615,7 @@ const API = (() => {
|
|||
profile(zwingername) { return get(`/breeder/profil/${encodeURIComponent(zwingername)}`); },
|
||||
mapMarkers() { return get('/breeder/map'); },
|
||||
updateProfile(data) { return put('/breeder/profile', data); },
|
||||
adminCreateProfile() { return post('/admin/breeder/create-profile', {}); },
|
||||
pendingList() { return get('/admin/breeders/pending'); },
|
||||
documents(userId) { return get(`/admin/breeder/${userId}/documents`); },
|
||||
documentUrl(userId, docId) { return `/api/admin/breeder/${userId}/document/${docId}`; },
|
||||
|
|
@ -631,6 +632,7 @@ const API = (() => {
|
|||
create(data) { return post('/litters', data); },
|
||||
update(id, data) { return put(`/litters/${id}`, data); },
|
||||
remove(id) { return del(`/litters/${id}`); },
|
||||
welfareConfirm(id) { return post(`/litters/${id}/welfare-confirm`, {}); },
|
||||
// Welpen
|
||||
puppies(id) { return get(`/litters/${id}/puppies`); },
|
||||
addPuppy(id, data) { return post(`/litters/${id}/puppies`, data); },
|
||||
|
|
@ -653,43 +655,51 @@ const API = (() => {
|
|||
remove(id) { return del(`/breeder/photos/${id}`); },
|
||||
};
|
||||
|
||||
// Öffentliche API
|
||||
return {
|
||||
get, post, put, patch, del, upload,
|
||||
auth, dogs, diary, health, tieraerzte, poison,
|
||||
places, routes, walks, events, sitting, forum, lost, knigge, weather, push,
|
||||
friends, chat, webcal, importData, sharing, widget, notifications, services, ratings, sittingAccess, training, notes,
|
||||
// ----------------------------------------------------------
|
||||
// ZUCHTKARTEI (Hunde-Stammdaten, Gesundheit, Genetik, Titel)
|
||||
// ----------------------------------------------------------
|
||||
const zuchthunde = {
|
||||
// Hunde
|
||||
list() { return get('/zuchthunde'); },
|
||||
get(id) { return get(`/zuchthunde/${id}`); },
|
||||
create(data) { return post('/zuchthunde', data); },
|
||||
update(id, data) { return put(`/zuchthunde/${id}`, data); },
|
||||
remove(id) { return del(`/zuchthunde/${id}`); },
|
||||
pedigree(id, gen=4) { return get(`/zuchthunde/${id}/pedigree?generations=${gen}`); },
|
||||
// Gesundheitstests
|
||||
healthTests(id) { return get(`/zuchthunde/${id}/health-tests`); },
|
||||
addHealthTest(id, data) { return post(`/zuchthunde/${id}/health-tests`, data); },
|
||||
updateHealthTest(tid, data) { return put(`/zuchthunde/health-tests/${tid}`, data); },
|
||||
deleteHealthTest(tid) { return del(`/zuchthunde/health-tests/${tid}`); },
|
||||
// Gentests
|
||||
geneticTests(id) { return get(`/zuchthunde/${id}/genetic-tests`); },
|
||||
addGeneticTest(id, data) { return post(`/zuchthunde/${id}/genetic-tests`, data); },
|
||||
updateGeneticTest(tid, data) { return put(`/zuchthunde/genetic-tests/${tid}`, data); },
|
||||
deleteGeneticTest(tid) { return del(`/zuchthunde/genetic-tests/${tid}`); },
|
||||
// Titel
|
||||
titles(id) { return get(`/zuchthunde/${id}/titles`); },
|
||||
addTitle(id, data) { return post(`/zuchthunde/${id}/titles`, data); },
|
||||
updateTitle(tid, data) { return put(`/zuchthunde/titles/${tid}`, data); },
|
||||
deleteTitle(tid) { return del(`/zuchthunde/titles/${tid}`); },
|
||||
// Probeverpaarung
|
||||
trialMating(vaterId, mutterId) { return post('/zuchthunde/trial-mating', { vater_id: vaterId, mutter_id: mutterId }); },
|
||||
};
|
||||
|
||||
breeder, litters, breederPhotos, zuchthunde,
|
||||
// ----------------------------------------------------------
|
||||
// ZÜCHTER-KI
|
||||
// ----------------------------------------------------------
|
||||
const zuchtKi = {
|
||||
wurfankuendigung(litterId) { return post('/zucht-ki/wurfankuendigung', { litter_id: litterId }); },
|
||||
genetikErklaerung(litterId, ziel) { return post('/zucht-ki/genetik-erklaerung', { litter_id: litterId, zielgruppe: ziel }); },
|
||||
paarungAnalyse(vaterId, mutterId, ik, welfareLevel) {
|
||||
return post('/zucht-ki/paarung-analyse', { vater_id: vaterId, mutter_id: mutterId, ik_prozent: ik, welfare_level: welfareLevel });
|
||||
},
|
||||
hundBeschreibung(hundId) { return post('/zucht-ki/hund-beschreibung', { hund_id: hundId }); },
|
||||
jahresbericht() { return post('/zucht-ki/jahresbericht', {}); },
|
||||
};
|
||||
|
||||
// Öffentliche API
|
||||
return {
|
||||
get, post, put, patch, del, upload,
|
||||
auth, dogs, diary, health, tieraerzte, poison,
|
||||
places, routes, walks, events, sitting, forum, lost, knigge, weather, push,
|
||||
friends, chat, webcal, importData, sharing, widget, notifications, services, ratings, sittingAccess, training, notes,
|
||||
breeder, litters, breederPhotos, zuchthunde, zuchtKi,
|
||||
subscribeToPush, getLocation, clientNow,
|
||||
APIError,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '444'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '451'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,13 @@ window.Page_litters = (() => {
|
|||
});
|
||||
});
|
||||
|
||||
el.querySelectorAll('.litters-ki-announce-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = parseInt(btn.dataset.id);
|
||||
_showKiAnnouncement(id);
|
||||
});
|
||||
});
|
||||
|
||||
el.querySelectorAll('.litters-add-puppy-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = parseInt(btn.dataset.id);
|
||||
|
|
@ -249,6 +256,11 @@ window.Page_litters = (() => {
|
|||
title="Elterntier-Fotos verwalten">
|
||||
${UI.icon('users')} Eltern
|
||||
</button>
|
||||
${_appState.user?.ki_zucht_wurfankuendigung !== 0 ? `
|
||||
<button class="btn btn-ghost btn-sm litters-ki-announce-btn" data-id="${l.id}"
|
||||
title="KI: Wurfankündigung schreiben">
|
||||
${UI.icon('sparkle')} Ankündigung
|
||||
</button>` : ''}
|
||||
<button class="btn btn-ghost btn-sm litters-edit-btn" data-id="${l.id}"
|
||||
title="Bearbeiten">
|
||||
${UI.icon('pencil-simple')}
|
||||
|
|
@ -477,24 +489,42 @@ window.Page_litters = (() => {
|
|||
// ----------------------------------------------------------
|
||||
// Wurf-Formular (neu / bearbeiten)
|
||||
// ----------------------------------------------------------
|
||||
function _showLitterForm(litter) {
|
||||
async function _showLitterForm(litter) {
|
||||
const isEdit = !!litter;
|
||||
const v = litter || {};
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
|
||||
// Zuchtkartei laden für Elterntier-Auswahl
|
||||
let zuchthunde = [];
|
||||
try { zuchthunde = await API.zuchthunde.list(); } catch {}
|
||||
const maennlich = zuchthunde.filter(h => h.geschlecht !== 'weiblich');
|
||||
const weiblich = zuchthunde.filter(h => h.geschlecht !== 'maennlich');
|
||||
|
||||
const buildSelect = (name, idName, list, currentId, currentName, placeholder) => {
|
||||
const opts = list.map(h => {
|
||||
const label = h.name + (h.rufname ? ` (${h.rufname})` : '') + (h.zuchtbuchnummer ? ` · ${h.zuchtbuchnummer}` : '');
|
||||
return `<option value="${h.id}" data-name="${_esc(h.name)}" ${currentId == h.id ? 'selected' : ''}>${_esc(label)}</option>`;
|
||||
}).join('');
|
||||
return `
|
||||
<select class="form-control" name="${idName}" id="${idName}-sel" style="margin-bottom:var(--space-2)">
|
||||
<option value="">— ${placeholder} —</option>
|
||||
${opts}
|
||||
</select>
|
||||
<input class="form-control" type="text" name="${name}" id="${name}-txt"
|
||||
value="${_esc(currentName || '')}" placeholder="oder Namen frei eingeben">`;
|
||||
};
|
||||
|
||||
const body = `
|
||||
<form id="litter-form" autocomplete="off">
|
||||
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3)">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Vatername</label>
|
||||
<input class="form-control" type="text" name="vater_name"
|
||||
value="${_esc(v.vater_name || '')}" placeholder="Name des Vaters">
|
||||
<label class="form-label">Vater</label>
|
||||
${buildSelect('vater_name', 'vater_id', maennlich, v.vater_id, v.vater_name, 'Aus Zuchtkartei')}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Muttername</label>
|
||||
<input class="form-control" type="text" name="mutter_name"
|
||||
value="${_esc(v.mutter_name || '')}" placeholder="Name der Mutter">
|
||||
<label class="form-label">Mutter</label>
|
||||
${buildSelect('mutter_name', 'mutter_id', weiblich, v.mutter_id, v.mutter_name, 'Aus Zuchtkartei')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -583,6 +613,16 @@ window.Page_litters = (() => {
|
|||
|
||||
document.getElementById('lf-cancel')?.addEventListener('click', UI.modal.close);
|
||||
|
||||
// Auto-Fill: Dropdown → Namenfeld befüllen
|
||||
['vater', 'mutter'].forEach(role => {
|
||||
document.getElementById(`${role}_id-sel`)?.addEventListener('change', e => {
|
||||
const sel = e.target;
|
||||
const opt = sel.options[sel.selectedIndex];
|
||||
const txt = document.getElementById(`${role}_name-txt`);
|
||||
if (txt) txt.value = opt.value ? (opt.dataset.name || '') : '';
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('litter-form')?.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
const btn = document.getElementById('lf-submit');
|
||||
|
|
@ -591,6 +631,8 @@ window.Page_litters = (() => {
|
|||
const payload = {
|
||||
vater_name: fd.get('vater_name')?.trim() || null,
|
||||
mutter_name: fd.get('mutter_name')?.trim() || null,
|
||||
vater_id: fd.get('vater_id') ? parseInt(fd.get('vater_id')) : null,
|
||||
mutter_id: fd.get('mutter_id') ? parseInt(fd.get('mutter_id')) : null,
|
||||
geburt_datum: fd.get('geburt_datum') || null,
|
||||
erwartetes_datum: fd.get('erwartetes_datum') || null,
|
||||
welpen_gesamt: fd.get('welpen_gesamt') ? parseInt(fd.get('welpen_gesamt')) : null,
|
||||
|
|
@ -608,14 +650,16 @@ window.Page_litters = (() => {
|
|||
const updated = await API.litters.update(litter.id, payload);
|
||||
const idx = _litters.findIndex(l => l.id === litter.id);
|
||||
if (idx !== -1) _litters[idx] = updated;
|
||||
UI.modal.close();
|
||||
UI.toast.success('Wurf aktualisiert.');
|
||||
_renderList();
|
||||
} else {
|
||||
const created = await API.litters.create(payload);
|
||||
_litters.unshift(created);
|
||||
UI.toast.success('Wurf angelegt.');
|
||||
UI.modal.close();
|
||||
_renderList();
|
||||
_showWelfareModal(created.welfare, created.id);
|
||||
}
|
||||
UI.modal.close();
|
||||
_renderList();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -967,6 +1011,133 @@ window.Page_litters = (() => {
|
|||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// Tierschutz-Check Modal
|
||||
// ----------------------------------------------------------
|
||||
function _showWelfareModal(welfare, litterId) {
|
||||
if (!welfare) return;
|
||||
|
||||
const color = { ok: '#16a34a', info: '#3b82f6', warning: '#f59e0b', critical: '#dc2626' }[welfare.level] || '#6b7280';
|
||||
const title = { ok: 'Alles prima', info: 'Hinweis', warning: 'Bitte beachten', critical: 'Kritischer Hinweis' }[welfare.level];
|
||||
const icon = { ok: 'check-circle', info: 'info', warning: 'warning', critical: 'warning-circle' }[welfare.level];
|
||||
|
||||
const issueHTML = (welfare.issues || []).map(i => `
|
||||
<div style="display:flex;gap:8px;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||||
<span style="color:${color};flex-shrink:0">${UI.icon('warning')}</span>
|
||||
<span style="font-size:var(--text-sm)">${_esc(i.text)}</span>
|
||||
</div>`).join('');
|
||||
|
||||
const okHTML = (welfare.ok_points || []).map(p => `
|
||||
<div style="display:flex;gap:8px;padding:4px 0">
|
||||
<span style="color:#16a34a;flex-shrink:0">${UI.icon('check')}</span>
|
||||
<span style="font-size:var(--text-sm);color:var(--c-text-secondary)">${_esc(p)}</span>
|
||||
</div>`).join('');
|
||||
|
||||
const isProblematic = welfare.level === 'warning' || welfare.level === 'critical';
|
||||
|
||||
UI.modal.open({
|
||||
title: `<span style="color:${color}">${UI.icon(icon)} Tierschutz-Check: ${title}</span>`,
|
||||
body: `
|
||||
<div style="background:${color}18;border:1.5px solid ${color}40;border-radius:var(--radius-md);
|
||||
padding:var(--space-4);margin-bottom:var(--space-4)">
|
||||
${issueHTML || ''}
|
||||
${okHTML}
|
||||
</div>
|
||||
${welfare.level === 'critical' ? `
|
||||
<div style="font-size:var(--text-sm);color:var(--c-text-secondary);
|
||||
background:var(--c-surface-2);border-radius:var(--radius-sm);
|
||||
padding:var(--space-3)">
|
||||
${UI.icon('info')} Wenn du fortfährst, wird der Administrator informiert.
|
||||
</div>` : ''}
|
||||
`,
|
||||
footer: isProblematic ? `
|
||||
<div style="display:flex;gap:var(--space-2);width:100%">
|
||||
<button class="btn btn-secondary flex-1" id="welfare-back-btn">
|
||||
${UI.icon('arrow-left')} Zurück
|
||||
</button>
|
||||
<button class="btn btn-ghost flex-1" id="welfare-confirm-btn"
|
||||
style="color:${color}">
|
||||
Trotzdem fortfahren
|
||||
</button>
|
||||
</div>` : `
|
||||
<button class="btn btn-primary" data-modal-close style="width:100%">
|
||||
${UI.icon('check')} Verstanden
|
||||
</button>`,
|
||||
});
|
||||
|
||||
document.getElementById('welfare-back-btn')?.addEventListener('click', () => {
|
||||
UI.modal.close?.();
|
||||
const litter = _litters.find(l => l.id === litterId);
|
||||
API.litters.remove(litterId).catch(() => {});
|
||||
_litters = _litters.filter(l => l.id !== litterId);
|
||||
_renderList();
|
||||
setTimeout(() => _showLitterForm(null), 150);
|
||||
});
|
||||
|
||||
document.getElementById('welfare-confirm-btn')?.addEventListener('click', async () => {
|
||||
await API.litters.welfareConfirm(litterId).catch(() => {});
|
||||
UI.modal.close?.();
|
||||
UI.toast.info('Wurf gespeichert.');
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// KI: Wurfankündigung
|
||||
// ----------------------------------------------------------
|
||||
async function _showKiAnnouncement(litterId) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Wurfankündigung`,
|
||||
body: `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">
|
||||
KI schreibt Wurfankündigung…
|
||||
</p>`,
|
||||
footer: '',
|
||||
});
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
const result = await API.zuchtKi.wurfankuendigung(litterId);
|
||||
text = result.text || result.content || result.ankuendigung || JSON.stringify(result);
|
||||
} catch (err) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Wurfankündigung`,
|
||||
body: `<p style="color:var(--c-danger)">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Wurfankündigung`,
|
||||
body: `<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary flex-1" id="ki-announce-copy">
|
||||
${UI.icon('clipboard-text')} Kopieren
|
||||
</button>
|
||||
<button class="btn btn-primary flex-1" id="ki-announce-use">
|
||||
${UI.icon('check')} In Beschreibung übernehmen
|
||||
</button>`,
|
||||
});
|
||||
|
||||
document.getElementById('ki-announce-copy')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
UI.toast.success('Text kopiert.');
|
||||
} catch {
|
||||
UI.toast.error('Kopieren nicht möglich.');
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('ki-announce-use')?.addEventListener('click', async () => {
|
||||
const btn = document.getElementById('ki-announce-use');
|
||||
await UI.asyncButton(btn, async () => {
|
||||
await API.litters.update(litterId, { beschreibung: text });
|
||||
UI.modal.close();
|
||||
UI.toast.success('Beschreibung aktualisiert.');
|
||||
await _loadLitters();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return { init, refresh, onDogChange };
|
||||
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -685,6 +685,30 @@ window.Page_settings = (() => {
|
|||
_loadBreederCard();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// KI-Toggle-Zeile (Hilfsfunktion für Züchter-Card)
|
||||
// ----------------------------------------------------------
|
||||
function _kiToggleRow(key, label, user) {
|
||||
const active = user[key] !== 0;
|
||||
return `
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;
|
||||
padding:var(--space-2) 0;font-size:var(--text-sm)">
|
||||
<span>${_esc(label)}</span>
|
||||
<button class="by-toggle ki-toggle-btn" data-key="${_esc(key)}"
|
||||
data-active="${active ? '1' : '0'}"
|
||||
style="position:relative;display:inline-block;width:44px;height:24px;
|
||||
border:none;border-radius:12px;cursor:pointer;flex-shrink:0;
|
||||
background:${active ? 'var(--c-primary)' : 'var(--c-border)'};
|
||||
transition:background .2s">
|
||||
<span class="by-toggle-thumb"
|
||||
style="position:absolute;top:2px;left:${active ? '22px' : '2px'};
|
||||
width:20px;height:20px;border-radius:50%;
|
||||
background:#fff;transition:left .2s;
|
||||
box-shadow:0 1px 3px rgba(0,0,0,.3)"></span>
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// ZÜCHTER-CARD — asynchron laden und in Slot rendern
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -722,7 +746,30 @@ window.Page_settings = (() => {
|
|||
${rolle === 'breeder' && profile ? `
|
||||
<button class="btn btn-secondary btn-sm" id="breeder-edit-profile-btn" style="margin-top:var(--space-3)">
|
||||
${UI.icon('pencil-simple')} Profil bearbeiten
|
||||
</button>` : ''}`;
|
||||
</button>` : ''}
|
||||
${rolle === 'admin' && !profile ? `
|
||||
<button class="btn btn-primary btn-sm" id="breeder-admin-create-btn" style="margin-top:var(--space-3)">
|
||||
${UI.icon('plus')} Admin-Züchterprofil anlegen
|
||||
</button>` : ''}
|
||||
${rolle === 'admin' && profile ? `
|
||||
<button class="btn btn-secondary btn-sm" id="breeder-edit-profile-btn" style="margin-top:var(--space-3)">
|
||||
${UI.icon('pencil-simple')} Profil bearbeiten
|
||||
</button>` : ''}
|
||||
${profile ? `
|
||||
<div style="margin-top:var(--space-4);padding-top:var(--space-3);border-top:1px solid var(--c-border)">
|
||||
<div style="font-size:var(--text-xs);font-weight:600;color:var(--c-text-secondary);
|
||||
text-transform:uppercase;letter-spacing:0.05em;margin-bottom:var(--space-3)">
|
||||
KI-Züchter-Assistenz
|
||||
</div>
|
||||
${_kiToggleRow('ki_zucht_wurfankuendigung', 'Wurfankündigungen schreiben', _appState.user || {})}
|
||||
${_kiToggleRow('ki_zucht_genetik', 'Genetik-Erklärung für Käufer', _appState.user || {})}
|
||||
${_kiToggleRow('ki_zucht_paarung', 'Paarungsanalyse', _appState.user || {})}
|
||||
${_kiToggleRow('ki_zucht_beschreibung', 'Hunde-Beschreibungen', _appState.user || {})}
|
||||
${_kiToggleRow('ki_zucht_jahresbericht', 'Jahresauswertung', _appState.user || {})}
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:var(--space-2)">
|
||||
${UI.icon('info')} Der Tierschutz-Check läuft immer automatisch und ist nicht abschaltbar.
|
||||
</div>
|
||||
</div>` : ''}`;
|
||||
} else if (breeder_status === 'pending') {
|
||||
statusBadge = `<span class="badge" style="background:#f59e0b;color:#fff">
|
||||
${UI.icon('hourglass')} Antrag wird geprüft
|
||||
|
|
@ -770,6 +817,48 @@ window.Page_settings = (() => {
|
|||
slot.querySelector('#breeder-edit-profile-btn')?.addEventListener('click', () =>
|
||||
_openBreederEditModal(profile)
|
||||
);
|
||||
|
||||
slot.querySelector('#breeder-admin-create-btn')?.addEventListener('click', async (e) => {
|
||||
const btn = e.currentTarget;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Wird angelegt…';
|
||||
try {
|
||||
await API.breeder.adminCreateProfile();
|
||||
UI.toast.success('Admin-Züchterprofil angelegt. Bitte Seite neu laden.');
|
||||
_loadBreederCard();
|
||||
} catch (err) {
|
||||
UI.toast.error(err.message || 'Fehler beim Anlegen.');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = `${UI.icon('plus')} Admin-Züchterprofil anlegen`;
|
||||
}
|
||||
});
|
||||
|
||||
// KI-Toggle-Handler
|
||||
slot.querySelectorAll('.ki-toggle-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const key = btn.dataset.key;
|
||||
const active = btn.dataset.active === '1';
|
||||
const newVal = active ? 0 : 1;
|
||||
|
||||
// Optimistisches UI-Update
|
||||
btn.dataset.active = newVal ? '1' : '0';
|
||||
btn.style.background = newVal ? 'var(--c-primary)' : 'var(--c-border)';
|
||||
const thumb = btn.querySelector('.by-toggle-thumb');
|
||||
if (thumb) thumb.style.left = newVal ? '22px' : '2px';
|
||||
|
||||
try {
|
||||
const updated = await API.patch('/profile', { [key]: newVal });
|
||||
if (_appState?.user) _appState.user[key] = newVal;
|
||||
UI.toast.success(newVal ? 'KI-Feature aktiviert.' : 'KI-Feature deaktiviert.');
|
||||
} catch (err) {
|
||||
// Revert
|
||||
btn.dataset.active = active ? '1' : '0';
|
||||
btn.style.background = active ? 'var(--c-primary)' : 'var(--c-border)';
|
||||
if (thumb) thumb.style.left = active ? '22px' : '2px';
|
||||
UI.toast.error(err?.message || 'Einstellung konnte nicht gespeichert werden.');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -108,8 +108,16 @@ window.Page_zuchthunde = (() => {
|
|||
${UI.icon('plus')} Hund anlegen
|
||||
</button>
|
||||
<button class="btn btn-secondary btn-sm" id="zh-trial-btn">
|
||||
${UI.icon('dna')} Probeverpaarung
|
||||
${UI.icon('heart-fill')} Probeverpaarung
|
||||
</button>
|
||||
<a href="/api/breeder/export" download class="btn btn-ghost btn-sm" id="zh-export-btn"
|
||||
title="Alle Daten herunterladen (HTML + ODS)">
|
||||
${UI.icon('download-simple')} Export
|
||||
</a>
|
||||
${_appState?.user?.ki_zucht_jahresbericht !== 0 ? `
|
||||
<a class="btn btn-ghost btn-sm" id="zh-jahresbericht-btn">
|
||||
${UI.icon('chart-bar')} Jahresbericht
|
||||
</a>` : ''}
|
||||
</div>
|
||||
<div style="padding:0 0 var(--space-3)">
|
||||
<input class="form-control" id="zh-search" type="search"
|
||||
|
|
@ -123,6 +131,7 @@ window.Page_zuchthunde = (() => {
|
|||
|
||||
document.getElementById('zh-new-btn')?.addEventListener('click', () => _showHundForm(null));
|
||||
document.getElementById('zh-trial-btn')?.addEventListener('click', () => _showTrialMatingModal());
|
||||
document.getElementById('zh-jahresbericht-btn')?.addEventListener('click', () => _showJahresbericht());
|
||||
|
||||
document.getElementById('zh-search')?.addEventListener('input', e => {
|
||||
_query = e.target.value.toLowerCase().trim();
|
||||
|
|
@ -215,6 +224,13 @@ window.Page_zuchthunde = (() => {
|
|||
});
|
||||
});
|
||||
|
||||
el.querySelectorAll('.zh-ki-desc-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const id = parseInt(btn.dataset.id);
|
||||
_showKiDesc(id);
|
||||
});
|
||||
});
|
||||
|
||||
// Offene Sektionen wiederherstellen
|
||||
Object.entries(_openSections).forEach(([id, sec]) => {
|
||||
if (sec) _openSection(parseInt(id), sec);
|
||||
|
|
@ -259,6 +275,10 @@ window.Page_zuchthunde = (() => {
|
|||
title="Stammbaum">
|
||||
${UI.icon('tree-structure')} Stammbaum
|
||||
</button>
|
||||
${_appState.user?.ki_zucht_beschreibung !== 0 ? `
|
||||
<button class="btn btn-ghost btn-sm zh-ki-desc-btn" data-id="${h.id}">
|
||||
${UI.icon('sparkle')} Beschreibung
|
||||
</button>` : ''}
|
||||
<button class="btn btn-ghost btn-sm zh-link-btn" data-id="${h.id}"
|
||||
title="Profil-Link kopieren">
|
||||
${UI.icon('link-simple')}
|
||||
|
|
@ -1134,6 +1154,39 @@ window.Page_zuchthunde = (() => {
|
|||
}).join('')
|
||||
: `<li style="color:var(--c-text-muted)">Keine gemeinsamen Vorfahren gefunden.</li>`;
|
||||
|
||||
const welfare = result.welfare;
|
||||
let welfareHTML = '';
|
||||
if (welfare) {
|
||||
const wColor = { ok: '#16a34a', info: '#3b82f6', warning: '#f59e0b', critical: '#dc2626' }[welfare.level] || '#6b7280';
|
||||
const wTitle = { ok: 'Alles prima', info: 'Hinweis', warning: 'Bitte beachten', critical: 'Kritischer Hinweis' }[welfare.level];
|
||||
const wIcon = { ok: 'check-circle', info: 'info', warning: 'warning', critical: 'warning-circle' }[welfare.level];
|
||||
|
||||
const wIssueHTML = (welfare.issues || []).map(i => `
|
||||
<div style="display:flex;gap:8px;padding:6px 0;border-bottom:1px solid rgba(0,0,0,.06)">
|
||||
<span style="color:${wColor};flex-shrink:0">${UI.icon('warning')}</span>
|
||||
<span style="font-size:var(--text-sm)">${_esc(i.text)}</span>
|
||||
</div>`).join('');
|
||||
|
||||
const wOkHTML = (welfare.ok_points || []).map(p => `
|
||||
<div style="display:flex;gap:8px;padding:4px 0">
|
||||
<span style="color:#16a34a;flex-shrink:0">${UI.icon('check')}</span>
|
||||
<span style="font-size:var(--text-sm);color:var(--c-text-secondary)">${_esc(p)}</span>
|
||||
</div>`).join('');
|
||||
|
||||
welfareHTML = `
|
||||
<div>
|
||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-sm);margin-bottom:var(--space-2);
|
||||
color:${wColor}">
|
||||
${UI.icon(wIcon)} Tierschutz-Check: ${wTitle}
|
||||
</div>
|
||||
<div style="background:${wColor}18;border:1.5px solid ${wColor}40;border-radius:var(--radius-md);
|
||||
padding:var(--space-3)">
|
||||
${wIssueHTML || ''}
|
||||
${wOkHTML}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const body = `
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-4)">
|
||||
<div style="display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3);
|
||||
|
|
@ -1149,6 +1202,7 @@ window.Page_zuchthunde = (() => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${welfareHTML}
|
||||
<div>
|
||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-sm);margin-bottom:var(--space-2)">
|
||||
Gemeinsame Vorfahren
|
||||
|
|
@ -1159,11 +1213,22 @@ window.Page_zuchthunde = (() => {
|
|||
</div>
|
||||
</div>`;
|
||||
|
||||
const kiPaarungBtn = _appState?.user?.ki_zucht_paarung !== 0
|
||||
? `<button type="button" class="btn btn-secondary btn-sm" id="trial-ki-btn">
|
||||
${UI.icon('sparkle')} KI-Analyse anfordern
|
||||
</button>`
|
||||
: '';
|
||||
|
||||
const footer = `
|
||||
<button type="button" class="btn btn-secondary flex-1" id="zhresult-back">
|
||||
${UI.icon('arrow-left')} Zurück
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary flex-1" id="zhresult-close">Schließen</button>`;
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2);width:100%">
|
||||
${kiPaarungBtn}
|
||||
<div style="display:flex;gap:var(--space-2)">
|
||||
<button type="button" class="btn btn-secondary flex-1" id="zhresult-back">
|
||||
${UI.icon('arrow-left')} Zurück
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary flex-1" id="zhresult-close">Schließen</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('dna')} Ergebnis Probeverpaarung`,
|
||||
|
|
@ -1173,6 +1238,164 @@ window.Page_zuchthunde = (() => {
|
|||
|
||||
document.getElementById('zhresult-close')?.addEventListener('click', UI.modal.close);
|
||||
document.getElementById('zhresult-back')?.addEventListener('click', () => _showTrialMatingModal());
|
||||
document.getElementById('trial-ki-btn')?.addEventListener('click', () => _showKiPaarung(result));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// KI: Hund-Beschreibung
|
||||
// ----------------------------------------------------------
|
||||
async function _showKiDesc(hundId) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Hunde-Beschreibung`,
|
||||
body: `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">KI erstellt Beschreibung…</p>`,
|
||||
footer: '',
|
||||
});
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
const result = await API.zuchtKi.hundBeschreibung(hundId);
|
||||
text = result.text || result.content || result.beschreibung || JSON.stringify(result);
|
||||
} catch (err) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Hunde-Beschreibung`,
|
||||
body: `<p style="color:var(--c-danger)">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Hunde-Beschreibung`,
|
||||
body: `<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary flex-1" id="ki-desc-copy">
|
||||
${UI.icon('clipboard-text')} Kopieren
|
||||
</button>
|
||||
<button class="btn btn-primary flex-1" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
|
||||
document.getElementById('ki-desc-copy')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
UI.toast.success('Text kopiert.');
|
||||
} catch {
|
||||
UI.toast.error('Kopieren nicht möglich.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// KI: Jahresbericht
|
||||
// ----------------------------------------------------------
|
||||
async function _showJahresbericht() {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('chart-bar')} KI-Jahresbericht`,
|
||||
body: `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">KI analysiert deine Zuchtkartei…</p>`,
|
||||
footer: '',
|
||||
});
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
const result = await API.zuchtKi.jahresbericht();
|
||||
text = result.text || result.content || result.bericht || JSON.stringify(result);
|
||||
} catch (err) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('chart-bar')} KI-Jahresbericht`,
|
||||
body: `<p style="color:var(--c-danger)">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('chart-bar')} KI-Jahresbericht`,
|
||||
body: `<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary flex-1" id="ki-bericht-copy">
|
||||
${UI.icon('clipboard-text')} Kopieren
|
||||
</button>
|
||||
<button class="btn btn-primary flex-1" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
|
||||
document.getElementById('ki-bericht-copy')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
UI.toast.success('Bericht kopiert.');
|
||||
} catch {
|
||||
UI.toast.error('Kopieren nicht möglich.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// KI: Paarungsanalyse
|
||||
// ----------------------------------------------------------
|
||||
async function _showKiPaarung(trialResult) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Paarungsanalyse`,
|
||||
body: `<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-6)">KI analysiert die Verpaarung…</p>`,
|
||||
footer: '',
|
||||
});
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await API.zuchtKi.paarungAnalyse(
|
||||
trialResult.vater_id,
|
||||
trialResult.mutter_id,
|
||||
trialResult.ik_prozent,
|
||||
trialResult.welfare?.level
|
||||
);
|
||||
} catch (err) {
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Paarungsanalyse`,
|
||||
body: `<p style="color:var(--c-danger)">${_esc(err.message || 'Fehler beim Generieren.')}</p>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const empfehlung = result.empfehlung || result.recommendation || '';
|
||||
const text = result.text || result.content || result.analyse || JSON.stringify(result);
|
||||
|
||||
const empfehlungColor = {
|
||||
empfohlen: '#16a34a',
|
||||
bedingt: '#f59e0b',
|
||||
nicht_empfohlen: '#dc2626',
|
||||
}[empfehlung] || '#6b7280';
|
||||
|
||||
const empfehlungLabel = {
|
||||
empfohlen: 'Empfohlen',
|
||||
bedingt: 'Bedingt empfohlen',
|
||||
nicht_empfohlen: 'Nicht empfohlen',
|
||||
}[empfehlung] || empfehlung;
|
||||
|
||||
UI.modal.open({
|
||||
title: `${UI.icon('sparkle')} KI-Paarungsanalyse`,
|
||||
body: `
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-4)">
|
||||
${empfehlung ? `
|
||||
<div style="padding:var(--space-3);border-radius:var(--radius-md);
|
||||
background:${empfehlungColor}18;border:1.5px solid ${empfehlungColor}40;
|
||||
font-weight:var(--weight-semibold);color:${empfehlungColor}">
|
||||
${UI.icon('check-circle')} ${_esc(empfehlungLabel)}
|
||||
</div>` : ''}
|
||||
<div style="white-space:pre-wrap;font-size:var(--text-sm);line-height:1.6">${_esc(text)}</div>
|
||||
</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary flex-1" id="ki-paarung-copy">
|
||||
${UI.icon('clipboard-text')} Kopieren
|
||||
</button>
|
||||
<button class="btn btn-primary flex-1" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
|
||||
document.getElementById('ki-paarung-copy')?.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
UI.toast.success('Analyse kopiert.');
|
||||
} catch {
|
||||
UI.toast.error('Kopieren nicht möglich.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ban Yaro — Die deutschsprachige Hunde-Plattform</title>
|
||||
<meta name="description" content="Ban Yaro ist die kostenlose All-in-One Hunde-App für Deutschland, Österreich und die Schweiz. Tagebuch, Impfpass, Giftköder-Alarm, Gassi-Community, Hundesitting, Trainings-Tracker — DSGVO-konform, ohne App Store.">
|
||||
<title>Ban Yaro — Hunde-App für Besitzer, Züchter & Welpen-Käufer</title>
|
||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für Deutschland, Österreich und die Schweiz. Tagebuch, Impfpass, Wurfbörse, Stammbaum, Inzucht-Check, Giftköder-Alarm — DSGVO-konform, ohne App Store.">
|
||||
<meta name="keywords" content="Hunde App, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community">
|
||||
<meta name="robots" content="index, follow">
|
||||
<link rel="canonical" href="https://banyaro.app/info">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="Ban Yaro — Die deutschsprachige Hunde-Plattform">
|
||||
<meta property="og:description" content="Alles rund um deinen Hund — Tagebuch, Impfpass, Giftköder-Alarm, Gassi-Community, Hundesitting. Kostenlos, DSGVO-konform, ohne App Store.">
|
||||
<meta property="og:title" content="Ban Yaro — Hunde-App für Besitzer, Züchter & Welpen-Käufer">
|
||||
<meta property="og:description" content="Tagebuch, Impfpass, Wurfbörse, Stammbaum, Inzucht-Koeffizient, Giftköder-Alarm, Gassi-Community — alles in einer DSGVO-konformen App ohne App Store.">
|
||||
<meta property="og:url" content="https://banyaro.app/info">
|
||||
<meta property="og:image" content="https://banyaro.app/icons/icon-512.png">
|
||||
<meta property="og:locale" content="de_DE">
|
||||
|
|
@ -19,8 +20,8 @@
|
|||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Ban Yaro — Die deutschsprachige Hunde-Plattform">
|
||||
<meta name="twitter:description" content="Alles rund um deinen Hund — Tagebuch, Impfpass, Giftköder-Alarm, Gassi-Community. Kostenlos, DSGVO-konform.">
|
||||
<meta name="twitter:title" content="Ban Yaro — Hunde-App für Besitzer, Züchter & Welpen-Käufer">
|
||||
<meta name="twitter:description" content="Wurfbörse, Stammbaum, Inzucht-Koeffizient, Tierschutz-Check — und alles rund um deinen Hund. Kostenlos, DSGVO-konform.">
|
||||
<meta name="twitter:image" content="https://banyaro.app/icons/icon-512.png">
|
||||
|
||||
<!-- Structured Data -->
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
"@type": "MobileApplication",
|
||||
"name": "Ban Yaro",
|
||||
"alternateName": "Ban Yaro — Die Hunde-Plattform",
|
||||
"description": "Ban Yaro ist die kostenlose, deutschsprachige All-in-One Hunde-App. Digitales Tagebuch, Impfpass, Giftköder-Alarm, Gassi-Community, Hundesitting und mehr — DSGVO-konform, ohne App Store.",
|
||||
"description": "Ban Yaro ist die kostenlose, deutschsprachige All-in-One Hunde-App für Hundebesitzer und Züchter. Tagebuch, Impfpass, Wurfbörse, Stammbaum, Inzucht-Koeffizient, Tierschutz-Check, Giftköder-Alarm, Gassi-Community — DSGVO-konform, ohne App Store.",
|
||||
"url": "https://banyaro.app",
|
||||
"applicationCategory": "LifestyleApplication",
|
||||
"applicationSubCategory": "PetApplication",
|
||||
|
|
@ -69,11 +70,19 @@
|
|||
"Virtueller KI-Trainer mit täglichen Übungsempfehlungen und Fortschrittsprognose",
|
||||
"Wöchentlicher KI-Lober — jeden Montag 2-3 Sätze Lob für die Vorwoche",
|
||||
"Trainings-Gamification: Streaks, Abzeichen, Trainingskalender",
|
||||
"Kommandos & Fähigkeiten im Hundeprofil — praktisch für Hundesitter"
|
||||
"Kommandos & Fähigkeiten im Hundeprofil — praktisch für Hundesitter",
|
||||
"Wurfbörse — öffentliche Wurfankündigungen mit Filtersuche nach Rasse und Status",
|
||||
"Züchter-Profile mit verifizierten Gesundheitstests und Gentests",
|
||||
"Stammbaum-Visualisierung bis 4 Generationen",
|
||||
"Inzucht-Koeffizient nach Wright's Formel mit Ampel-Bewertung",
|
||||
"Probeverpaarung mit IK-Simulation und genetischer Risikoanalyse",
|
||||
"Tierschutz-Check automatisch bei jeder Verpaarung — nicht abschaltbar",
|
||||
"KI-Züchter-Assistenz: Wurfankündigungen, Genetik-Erklärung, Paarungsanalyse",
|
||||
"Datenexport als HTML und ODS — keine Datenfalle"
|
||||
],
|
||||
"screenshot": "https://banyaro.app/icons/icon-512.png",
|
||||
"softwareVersion": "2.0",
|
||||
"datePublished": "2026-04-25",
|
||||
"softwareVersion": "2.1",
|
||||
"datePublished": "2026-04-28",
|
||||
"areaServed": ["DE", "AT", "CH"],
|
||||
"audience": {
|
||||
"@type": "Audience",
|
||||
|
|
@ -386,6 +395,7 @@
|
|||
<div class="container">
|
||||
<span class="nav-brand">Ban Yaro</span>
|
||||
<a href="#funktionen">Funktionen</a>
|
||||
<a href="#zuechter">Züchter</a>
|
||||
<a href="#vergleich">Vergleich</a>
|
||||
<a href="#preise">Preise</a>
|
||||
<a href="#warum">Warum Ban Yaro?</a>
|
||||
|
|
@ -516,6 +526,67 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-group">
|
||||
<div class="feature-group-label">Für Züchter</div>
|
||||
<div class="feature-grid">
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🐾</span>
|
||||
<div><h3>Wurfbörse</h3><p>Öffentliche Wurfankündigungen mit Filter nach Rasse und Status. Interessenten schreiben direkt per Nachricht an. Für Käufer kostenlos.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🌳</span>
|
||||
<div><h3>Stammbaum</h3><p>4 Generationen visuell dargestellt. Klickbare Knoten öffnen das Hunde-Profil. Teilen per Link für Käufer-Dokumentation.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🧬</span>
|
||||
<div><h3>Inzucht-Koeffizient</h3><p>Automatische Berechnung nach Wright's Formel. Ampel-Bewertung: optimal unter 2,5%, kritisch ab 12,5%. Probeverpaarung simuliert jeden beliebigen Anpaarungspartner.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🩺</span>
|
||||
<div><h3>Gesundheitsdokumentation</h3><p>HD, ED, Augen, Herz, DNA-Tests — alle Nachweise strukturiert erfasst. Farbcodierte Ergebnis-Badges auf einen Blick.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🛡️</span>
|
||||
<div><h3>Tierschutz-Check</h3><p>Automatische Prüfung bei jeder Verpaarung: Alter, Wurfhäufigkeit, Deckpause, genetische Risiken. Nicht abschaltbar — weil die Tiere zählen.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">🤖</span>
|
||||
<div><h3>KI-Assistenz</h3><p>Wurfankündigungen schreiben, Genetik-Erklärungen für Käufer formulieren, Paarungsanalyse mit Empfehlung, Jahresauswertung. Nutzt Claude Sonnet direkt.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">📊</span>
|
||||
<div><h3>Datenexport</h3><p>Alle Zuchtkartei-Daten als HTML-Dossier (druckbar, mit Stammbaum-Visualisierung) und ODS-Tabelle (editierbar in LibreOffice/Excel). Keine Datenfalle.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<span class="feature-icon">📄</span>
|
||||
<div><h3>Kaufvertrag</h3><p>Automatisch ausgefüllter Kaufvertrag pro Welpe als druckbares Dokument — mit Chip-Nummer, Geburtsdatum, Käufer- und Züchterdaten.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="zuechter" style="background: linear-gradient(135deg, #7c3aed08 0%, #a78bfa10 100%); border-top: 1px solid #ede9fe; border-bottom: 1px solid #ede9fe;">
|
||||
<div class="container">
|
||||
<h2>Die Plattform für verantwortungsvolle Züchter</h2>
|
||||
<p class="section-intro">Ban Yaro ist die erste Hunde-App die Zucht-Management, Tierschutz-Checks und KI-Assistenz in einer Plattform verbindet — gedacht für Züchter die ihre Tiere ernst nehmen.</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem; margin-top: 2rem;">
|
||||
|
||||
<div style="background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #ede9fe;">
|
||||
<div style="font-size: 2rem; margin-bottom: 0.75rem;">🐕🦺</div>
|
||||
<h3 style="color: #7c3aed; margin-bottom: 0.5rem;">Für Käufer</h3>
|
||||
<p style="color: #4b5563; font-size: 0.95rem; line-height: 1.6;">Finde deinen Welpen in der Wurfbörse mit vollem Einblick in Gesundheitstests, Gentests und Stammbaum der Eltern. Schreibe direkt mit dem Züchter. Keine versteckten Händler.</p>
|
||||
</div>
|
||||
|
||||
<div style="background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #ede9fe;">
|
||||
<div style="font-size: 2rem; margin-bottom: 0.75rem;">✅</div>
|
||||
<h3 style="color: #7c3aed; margin-bottom: 0.5rem;">Transparenz als Standard</h3>
|
||||
<p style="color: #4b5563; font-size: 0.95rem; line-height: 1.6;">Verifizierte Züchter-Profile mit öffentlich sichtbaren Gesundheitsdaten. Der Tierschutz-Check läuft bei jeder Verpaarung automatisch — Ergebnisse gehen direkt an den Admin wenn kritische Grenzen überschritten werden.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Ban Yaro — Die deutschsprachige Hunde-Plattform
|
||||
# https://banyaro.app
|
||||
# Letzte Aktualisierung: 2026-04-25
|
||||
# Letzte Aktualisierung: 2026-04-28
|
||||
|
||||
## Was ist Ban Yaro?
|
||||
|
||||
|
|
@ -15,11 +15,12 @@ Ban Yaro ist kostenlos nutzbar (Freemium-Modell). Die App ist auf allen Smartpho
|
|||
## Zielgruppe
|
||||
|
||||
- Deutschsprachige Hundebesitzer (Deutschland, Österreich, Schweiz)
|
||||
- Verantwortungsvolle Hundezüchter (VDH und andere Verbände)
|
||||
- Welpen-Interessenten und Käufer
|
||||
- Hundeschulen und Hundetrainer
|
||||
- Tierärzte und Praxen
|
||||
- Züchter
|
||||
|
||||
## Funktionen (aktuell verfügbar)
|
||||
## Funktionen
|
||||
|
||||
### Hunde-Profil & Tagebuch
|
||||
- Digitales Hunde-Profil (Name, Rasse, Geburtstag, Foto, Chip-Nummer)
|
||||
|
|
@ -37,175 +38,148 @@ Ban Yaro ist kostenlos nutzbar (Freemium-Modell). Die App ist auf allen Smartpho
|
|||
- Printbarer Heimtierausweis (PDF)
|
||||
|
||||
### Pflege-System
|
||||
- 43 rassenspezifische Pflegetipps in 10 Kategorien: Fell, Krallen, Zähne, Ohren, Augen, Pfoten, Parasiten, Saisonal, Gesundheitsvorsorge, Welpen-Pflege
|
||||
- Fell-Typ-Unterscheidung: kurz / lang / lockig / Doppelmantel
|
||||
- Unterscheidung Schneiden vs. Trimmen
|
||||
- Tipp des Tages automatisch nach Rasse und Fell-Typ ausgewählt
|
||||
- Rassen-Autocomplete im Profil verknüpft mit Pflege-Tipps
|
||||
- 43 rassenspezifische Pflegetipps in 10 Kategorien
|
||||
- Fell-Typ-Unterscheidung, Schneiden vs. Trimmen
|
||||
- Tipp des Tages automatisch nach Rasse ausgewählt
|
||||
|
||||
### Training & KI-Trainer
|
||||
- Tägliches Trainings-Tagebuch (Wiederholungen, Erfolgsquote 0–100%, Hundestimmung, Zufriedenheit)
|
||||
- Übungsfortschritt in 5 Stufen — von "noch nicht gezeigt" bis "sitzt sicher"
|
||||
- Virtueller KI-Trainer: analysiert letzte 20 Sessions, empfiehlt täglich welche Übungen anstehen
|
||||
- Fortschrittsprognose bis zur Meisterschaft (Trendanalyse)
|
||||
- 104 Übungen in 7 Kategorien
|
||||
- KI-Trainingsplan erstellen (Plus-Feature)
|
||||
- Trainingskalender im Habit-Tracker-Stil
|
||||
- Gamification: Streaks, Abzeichen, XP
|
||||
- Kommandos & Fähigkeiten sichtbar im Hunde-Profil (für Hundesitter)
|
||||
- Tägliches Trainings-Tagebuch (Wiederholungen, Erfolgsquote, Hundestimmung)
|
||||
- Übungsfortschritt in 5 Stufen, 104 Übungen in 7 Kategorien
|
||||
- Virtueller KI-Trainer: analysiert letzte 20 Sessions, tägliche Empfehlung
|
||||
- Fortschrittsprognose bis zur Meisterschaft
|
||||
- Gamification: Streaks, Abzeichen, Trainingskalender
|
||||
|
||||
### Wöchentlicher Lober (KI)
|
||||
- Jeden Montag schreibt die KI automatisch 2-3 Sätze Lob für die Trainingsvorwoche
|
||||
- Nur Lob, kein Rat, kein Druck — positive Bestärkung
|
||||
- Basiert auf den geloggten Trainingseinheiten der Vorwoche
|
||||
### Züchter-Plattform (vollständig)
|
||||
|
||||
### Wetter & Zecken-Warnung
|
||||
- Wetter-Chip direkt in der App (Open-Meteo API, ohne API-Key)
|
||||
- Zecken-Warnung regelbasiert: aktiv März–Oktober bei Temperatur >7°C
|
||||
- Push-Benachrichtigungen für Zecken-Saison
|
||||
Ban Yaro ist die erste Hunde-App mit vollständiger Züchter-Unterstützung:
|
||||
|
||||
### Giftköder-Alarm
|
||||
- Giftköder-Meldungen mit GPS-Koordinaten und Foto
|
||||
- Push-Benachrichtigung für alle Nutzer im konfigurierbaren Umkreis
|
||||
- Interaktive Karte (OpenStreetMap/Leaflet)
|
||||
- Automatisches Ablaufdatum nach 7 Tagen
|
||||
**Züchter-Verifizierung:**
|
||||
- Antrag mit Dokumenten-Upload (VDH-Ausweis, Zuchtzulassung)
|
||||
- Admin-Prüfung und Freischaltung
|
||||
- Verifiziertes Züchter-Profil mit öffentlicher Seite (banyaro.app/breeder/{zwingername})
|
||||
|
||||
### Sicherheit & Community-Alerts
|
||||
- Verlorener Hund: Alert mit Foto und letzter GPS-Position
|
||||
- Nearby-Alerts: Push-Benachrichtigungen für Ereignisse in der Nähe
|
||||
**Wurfbörse:**
|
||||
- Öffentliche Wurfankündigungen für alle Nutzer zugänglich (banyaro.app/wurfboerse)
|
||||
- Filtersuche nach Rasse und Status (geplant / verfügbar / geboren)
|
||||
- Käufer schreiben direkt per integriertem Chat an den Züchter
|
||||
- Vollständige Eltern-Dokumentation sichtbar: Gesundheitstests, Gentests, Stammbaum
|
||||
|
||||
### NFC-Halsband-Tags
|
||||
- Jeder Hund hat eine öffentliche URL (ohne Login sichtbar)
|
||||
- "Ich habe diesen Hund gefunden"-Button → Besitzer bekommt Push-Benachrichtigung
|
||||
- Notfallkontakt ohne Telefonnummer preiszugeben
|
||||
- Physische NFC-Tags erhältlich (Shop)
|
||||
**Wurfverwaltung:**
|
||||
- CRUD für Würfe und einzelne Welpen
|
||||
- Gewichtsverlauf pro Welpe
|
||||
- Foto-System mit Sichtbarkeits-Stufen: öffentlich / nach Anfrage / privat
|
||||
- Automatisch ausgefüllter Kaufvertrag als druckbares HTML-Dokument
|
||||
|
||||
### Gassi-Community
|
||||
- Gassi-Treffen erstellen und beitreten
|
||||
- GPS-Routen aufzeichnen und teilen (mit Anti-Cheat-Validierung)
|
||||
- Routen bewerten (Untergrund, Schatten, Leinenpflicht, Sicherheit)
|
||||
- Beliebte Routen entdecken
|
||||
**Zuchtkartei:**
|
||||
- Hunde-Stammdaten: Name, Rufname, Chip, Zuchtbuchnummer, Eltern (Vater/Mutter-Verknüpfung)
|
||||
- Gesundheitstests: HD, ED, OCD, Augen, Herz, Patella, ZTP — mit farbigen Ergebnis-Badges
|
||||
- Genetische Tests: MDR1, PRA, DM, vWD und weitere DNA-Marker (clear/carrier/affected)
|
||||
- Titel & Auszeichnungen: CAC, CACIB, BOB, IPO, BH — chronologisch mit Richter und Ort
|
||||
|
||||
### Hundesitting-Netzwerk
|
||||
- Sitter-Profile mit Erfahrung und Bewertungen
|
||||
- Buchungsanfragen und Kalender
|
||||
- Nur 8% Provision (vs. 20% bei Rover/Pawshake)
|
||||
- Bewertungen verifizierter Buchungen
|
||||
**Stammbaum:**
|
||||
- Visualisierung bis 4 Generationen als horizontales CSS-Grid
|
||||
- Klickbare Knoten navigieren zum jeweiligen Hunde-Profil
|
||||
- Teilen-Link für Käufer-Dokumentation
|
||||
- Öffentliches Hunde-Profil (banyaro.app/zucht-profil?id={id})
|
||||
|
||||
### Forum
|
||||
- Rassen-basierte Foren
|
||||
- KI-Zusammenfassung langer Threads
|
||||
- Experten-Badge (Tierarzt, Trainer)
|
||||
**Inzucht-Koeffizient:**
|
||||
- Automatische Berechnung nach Wright's Formel (bis 8 Generationen)
|
||||
- Ampel-Bewertung: optimal <2,5% / akzeptabel <6,25% / erhöht <12,5% / kritisch ≥12,5%
|
||||
- Probeverpaarung: simuliert beliebige Anpaarung ohne Speicherung
|
||||
|
||||
### Hunde-Wiki — Rassendatenbank
|
||||
- 1003 Hunderassen, 97,6% KI-angereichert via Wikipedia-grounded Recherche
|
||||
- Inhalte: Charakter, Größe, Aktivität, Eignung, Lebensdauer, Temperament
|
||||
- Community-Fotos im Wiki: User können Fotos einreichen (mit Bildrechte-Bestätigung)
|
||||
- Moderatoren geben Community-Fotos frei, anschließend Galerie-Ansicht
|
||||
- Wiki-Foto-Badge als Gamification-Belohnung für Foto-Einreicher
|
||||
- "Passt diese Rasse zu mir?" Quiz für angehende Hundebesitzer
|
||||
**Tierschutz-Check (immer aktiv, nicht abschaltbar):**
|
||||
- Läuft automatisch bei jeder Verpaarung und jedem neuen Wurf
|
||||
- Prüft: IK, Alter der Zuchthündin (min. 18 Monate), Deckpause (min. 12 Monate),
|
||||
Wurfanzahl (max. 4 empfohlen, kritisch ab 6), genetische Risiken
|
||||
- Farbcodierte Rückmeldung: grün (alles ok) / gelb (Hinweis) / rot (kritisch)
|
||||
- Bei "trotzdem fortfahren" auf rotem Befund: automatische Admin-Benachrichtigung
|
||||
- Philosophie: informieren statt blockieren, aber volle Transparenz und Accountability
|
||||
|
||||
### Hunde-Knigge
|
||||
- Ratgeber für Begegnungen (fremder Hund, Kinder, Radfahrer)
|
||||
- Regeln in ÖPNV und öffentlichen Orten
|
||||
- Haftpflicht-Ratgeber
|
||||
**KI-Züchter-Assistenz:**
|
||||
- Wurfankündigungen schreiben (KI generiert Text aus Eltern-Profilen)
|
||||
- Genetik-Erklärung für Käufer (verständliche Sprache) und Züchter (fachlich)
|
||||
- Paarungsanalyse mit Empfehlung (empfohlen / bedingt / nicht empfohlen)
|
||||
- Hunde-Beschreibungen für öffentliche Profile
|
||||
- Jahresbericht mit Trends und Empfehlungen
|
||||
- Privilegierte Rollen (Züchter, Moderatoren, Admins) nutzen Claude Sonnet direkt
|
||||
|
||||
### Events & Kultur
|
||||
- Agility-Turniere und Hundeausstellungen (VDH-Import)
|
||||
**Datenexport:**
|
||||
- Vollständiger Export als ZIP: HTML-Dossier (druckbar, Stammbaum-Visualisierung)
|
||||
und ODS-Tabelle (editierbar in LibreOffice/Excel)
|
||||
- 7 Tabellenblätter: Hunde, Gesundheitstests, Gentests, Titel, Würfe, Welpen, Gewichte
|
||||
- Keine Datenfalle: Züchter können jederzeit alle eigenen Daten exportieren
|
||||
|
||||
### Community-Features
|
||||
- Giftköder-Alarm mit Push-Benachrichtigungen
|
||||
- Verlorener Hund Alarm
|
||||
- Gassi-Treffen organisieren und finden
|
||||
- GPS-Routen aufzeichnen, teilen, bewerten
|
||||
- Hundesitting-Netzwerk (nur 8% Provision vs. 20% bei Rover/Pawshake)
|
||||
- Forum mit Rassen-basierten Unterforen
|
||||
- Direktnachrichten / Chat
|
||||
- Freundschaften und Nutzer-Profile
|
||||
|
||||
### Wissen
|
||||
- Hunde-Wiki: 1003 Hunderassen, Wikipedia-grounded, KI-angereichert
|
||||
- Community-Fotos mit Bildrechte-Bestätigung und Moderation
|
||||
- Hunde-Knigge (Begegnungen, ÖPNV, Haftpflicht)
|
||||
- Hundefilme-Datenbank mit "Stirbt der Hund?"-Rubrik
|
||||
- Veranstaltungskalender
|
||||
|
||||
### Hundefreundliche Orte
|
||||
- Crowd-sourced Datenbank hundefreundlicher Orte
|
||||
- Restaurants, Parks, Geschäfte
|
||||
- Detaillierte Bewertungen
|
||||
|
||||
### Gamification & Push
|
||||
- Badges, Streaks, XP — trägt zur Nutzerbindung bei
|
||||
- Wiki-Foto-Badge für Community-Foto-Beiträge
|
||||
- Push-Notifications für Alerts, Erinnerungen, Wöchentlicher Lober
|
||||
- Offline-Modus via Service Worker
|
||||
- Erste Hilfe Notfallratgeber
|
||||
|
||||
## KI-Integration
|
||||
|
||||
Ban Yaro nutzt KI an mehreren Stellen der Plattform:
|
||||
|
||||
- **Lokale KI**: LM Studio (Gemma-4-31B) auf eigenem Server — für datenschutzkritische Anfragen
|
||||
- **Cloud-KI**: Claude (Anthropic, Modell: claude-sonnet-4-6) als Fallback und für rechenintensive Aufgaben
|
||||
- **Symptom-Checker**: KI-gestützte Ersteinschätzung (kostenlos)
|
||||
- **Virtueller KI-Trainer**: Analysiert letzte 20 Trainings-Sessions, erstellt täglich priorisierte Übungsempfehlung
|
||||
- **Wöchentlicher Lober**: Vollautomatisch jeden Montag per APScheduler, lobt die Vorwoche in 2-3 Sätzen
|
||||
- **Breed-Enricher**: Wikipedia-grounded Anreicherung von 1003 Rassen-Datensätzen (97,6% abgeschlossen)
|
||||
- **KI-Trainingsplan** (Plus-Feature): Erstellt individuellen Trainingsplan auf Basis von Hund und Fortschritt
|
||||
Ban Yaro nutzt KI an mehreren Stellen:
|
||||
- **Privilegierte Nutzer** (Züchter, Moderatoren, Admins): Claude Sonnet (Anthropic) primär
|
||||
- **Standard-Nutzer**: Lokales LLM (LM Studio, Gemma-4-31B) primär, Claude als Fallback
|
||||
- **Tierschutz-Check**: Regelbasiert, keine KI — läuft immer zuverlässig
|
||||
- **Symptom-Checker, KI-Trainer, Lober**: Für alle kostenfrei
|
||||
- **Züchter-KI**: Wurfankündigungen, Genetik-Erklärungen, Paarungsanalyse, Jahresbericht
|
||||
|
||||
## Technologie
|
||||
|
||||
- Progressive Web App (PWA) — installierbar ohne App Store
|
||||
- Offline-fähig via Service Worker (Cache-Strategie mit Versionierung)
|
||||
- Backend: Python/FastAPI + SQLite
|
||||
- Frontend: Vanilla JS, kein Framework
|
||||
- Karten: Leaflet.js + OpenStreetMap (kein Google Maps, kein API-Key)
|
||||
- Wetter: Open-Meteo (kein API-Key erforderlich)
|
||||
- Karten: Leaflet.js + OpenStreetMap
|
||||
- Hosting: Deutschland (DSGVO-konform)
|
||||
- Analytics: Umami v2 (cookieless, DSGVO-konform)
|
||||
- KI lokal: LM Studio (Gemma-4-31B) auf eigenem Server
|
||||
- KI lokal: LM Studio (Gemma-4-31B)
|
||||
- KI Cloud: Claude API (claude-sonnet-4-6, Anthropic)
|
||||
- Push-Notifications: Web Push (VAPID)
|
||||
|
||||
## Monetarisierung
|
||||
|
||||
**Kostenlos (immer):**
|
||||
- Hunde-Profile
|
||||
- Tagebuch (unbegrenzte Einträge)
|
||||
- Pflege-System (43 rassenspezifische Tipps)
|
||||
- Symptom-Checker (KI)
|
||||
- Giftköder-Alarm & Zecken-Warnung
|
||||
- Verlorener Hund Alarm
|
||||
- Wiki & Knigge (1003 Rassen)
|
||||
- Training-Logging & KI-Trainer
|
||||
- Wöchentlicher Lober
|
||||
- Forum & Community
|
||||
- Gassi-Treffen & Routen
|
||||
- NFC-Halsband-Profil
|
||||
- Heimtierausweis (Druck)
|
||||
**Kostenlos:**
|
||||
- Alle Basis-Features inkl. Züchter-Antrag, Wurfverwaltung, Stammbaum, Tierschutz-Check
|
||||
|
||||
**Ban Yaro Plus (ca. 4,99 €/Monat) — in Entwicklung:**
|
||||
- Alles aus Kostenlos
|
||||
- KI-Trainingsplan erstellen
|
||||
- Erweiterte Statistiken & Fortschrittsanalyse
|
||||
**Züchter-Provision** (geplant): Wurfbörse bleibt für Käufer kostenlos
|
||||
|
||||
**Provisionen:**
|
||||
- Hundesitting: 8% Provision (Rover/Pawshake: 20%)
|
||||
**Ban Yaro Plus** (ca. 4,99 €/Monat, in Entwicklung):
|
||||
- KI-Trainingsplan, erweiterte Statistiken
|
||||
|
||||
**Physische Produkte:**
|
||||
- NFC-Halsband-Tags (ab ca. 6 €)
|
||||
**Hundesitting**: 8% Provision
|
||||
|
||||
## Community-Features
|
||||
## Öffentliche Seiten (ohne Login)
|
||||
|
||||
- Forum mit Rassen-basierten Unterforen
|
||||
- Community-Fotos im Rassen-Wiki (Einreichung, Moderation, Freigabe)
|
||||
- Gassi-Treffen organisieren und finden
|
||||
- GPS-Routen teilen und bewerten
|
||||
- Hundesitting-Netzwerk (Bewertungen, verifizierte Buchungen)
|
||||
- Gamification: Badges, Streaks, XP, Wiki-Foto-Badge
|
||||
- https://banyaro.app — Landing Page
|
||||
- https://banyaro.app/info — Landing Page (Alias)
|
||||
- https://banyaro.app/wiki/rassen — Alle Hunderassen
|
||||
- https://banyaro.app/wiki/rasse/{slug} — Rassen-Detail
|
||||
- https://banyaro.app/wurfboerse — Öffentliche Wurfbörse (Welpen suchen)
|
||||
- https://banyaro.app/breeder/{zwingername} — Öffentliches Züchter-Profil
|
||||
- https://banyaro.app/knigge — Hunde-Knigge
|
||||
- https://banyaro.app/hund/{id} — Öffentliches Hunde-Profil (NFC-Tag)
|
||||
|
||||
## Vergleich mit Konkurrenz
|
||||
## Öffentliche APIs
|
||||
|
||||
| Funktion | Ban Yaro | Dogorama | PetDesk | Tractive |
|
||||
|----------|----------|----------|---------|----------|
|
||||
| Kostenlos nutzbar | Ja | Begrenzt | Nein | Nein |
|
||||
| DSGVO / EU-Hosting | Ja | Nein | Nein | Teilweise |
|
||||
| Giftköder-Alarm | Ja | Nein | Nein | Nein |
|
||||
| Gassi-Community | Ja | Ja | Nein | Nein |
|
||||
| Hundesitting | Ja (8%) | Nein | Nein | Nein |
|
||||
| Digitaler Impfpass | Ja | Nein | Ja | Nein |
|
||||
| NFC-Halsband-Tag | Ja | Nein | Nein | Nein |
|
||||
| Pflege-Tipps rassenspezifisch | Ja | Nein | Nein | Nein |
|
||||
| Rassen-Wiki (1003, KI-angereichert) | Ja | Nein | Nein | Nein |
|
||||
| Symptom-Checker (kostenlos) | Ja | Nein | Nein | Nein |
|
||||
| Offline-Modus | Ja | Nein | Nein | Nein |
|
||||
| Kein App Store | Ja | Nein | Nein | Nein |
|
||||
| Sitting-Provision | 8% | – | – | – |
|
||||
- GET https://banyaro.app/api/wiki/rassen — Liste aller Hunderassen
|
||||
- GET https://banyaro.app/api/wiki/rassen/{slug} — Rassen-Detail
|
||||
- GET https://banyaro.app/api/litters — Öffentliche Wurfankündigungen
|
||||
- GET https://banyaro.app/api/breeder/profil/{zwingername} — Züchter-Profil
|
||||
- GET https://banyaro.app/api/events — Aktuelle Hundeevents
|
||||
- GET https://banyaro.app/api/poison — Aktuelle Giftköder-Meldungen
|
||||
- GET https://banyaro.app/api/lost — Aktuelle Vermisst-Meldungen
|
||||
- GET https://banyaro.app/api/knigge/articles — Hunde-Knigge Artikel
|
||||
- GET https://banyaro.app/api/stats — Community-Statistiken
|
||||
|
||||
## Domains
|
||||
|
||||
|
|
@ -216,14 +190,3 @@ Ban Yaro nutzt KI an mehreren Stellen der Plattform:
|
|||
|
||||
Website: https://banyaro.app
|
||||
E-Mail: Über das Kontaktformular in der App
|
||||
|
||||
## Öffentliche Daten-APIs (keine Authentifizierung nötig)
|
||||
|
||||
- GET https://banyaro.app/api/wiki/rassen — Liste aller Hunderassen (1003 Einträge)
|
||||
- GET https://banyaro.app/api/wiki/rassen/{slug} — Details zu einer Rasse
|
||||
- GET https://banyaro.app/api/events — Aktuelle Hundeevents
|
||||
- GET https://banyaro.app/api/poison — Aktuelle Giftköder-Meldungen
|
||||
- GET https://banyaro.app/api/lost — Aktuelle Vermisst-Meldungen
|
||||
- GET https://banyaro.app/api/knigge/articles — Hunde-Knigge Artikel
|
||||
- GET https://banyaro.app/api/movies/list — Hundefilme-Datenbank
|
||||
- GET https://banyaro.app/api/stats — Community-Statistiken
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ Allow: /info
|
|||
Allow: /wiki/rassen
|
||||
Allow: /wiki/rasse/
|
||||
Allow: /hund/
|
||||
Allow: /breeder/
|
||||
Allow: /wurfboerse
|
||||
Allow: /knigge
|
||||
Disallow: /api/
|
||||
Disallow: /ausweis/
|
||||
Disallow: /teilen/
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v465';
|
||||
const CACHE_VERSION = 'by-v474';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue