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
|
|
@ -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.');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue