/* ============================================================
BAN YARO — Hundesitting
Sitter suchen · Profil anbieten · Anfragen verwalten
============================================================ */
window.Page_sitting = (() => {
// ----------------------------------------------------------
// Konstanten
// ----------------------------------------------------------
const SERVICES = [
{ id: 'tagesbetreuung', label: 'Tagesbetreuung', icon: '☀️' },
{ id: 'uebernachtung', label: 'Übernachtung', icon: '🌙' },
{ id: 'gassi', label: 'Gassi gehen', icon: '🦮' },
{ id: 'hausbesuch', label: 'Hausbesuch', icon: '🏠' },
];
// ----------------------------------------------------------
// State
// ----------------------------------------------------------
let _container = null;
let _state = null;
let _tab = 'suchen'; // suchen | profil | anfragen
let _sitters = [];
let _mySitter = null;
let _myRequests = [];
let _inbox = [];
// ----------------------------------------------------------
// init
// ----------------------------------------------------------
async function init(container, appState) {
_container = container;
_state = appState;
_render();
await _load();
}
async function refresh() { await _load(); }
// ----------------------------------------------------------
// Render Grundstruktur
// ----------------------------------------------------------
function _render() {
_container.innerHTML = `
${_state.user ? `
` : ''}
`;
_container.addEventListener('click', _onClick);
}
// ----------------------------------------------------------
// Daten laden
// ----------------------------------------------------------
async function _load() {
const content = document.getElementById('sit-content');
if (!content) return;
content.innerHTML = UI.skeleton(3);
const tasks = [API.sitting.list()];
if (_state.user) {
tasks.push(API.sitting.me());
tasks.push(API.sitting.requests());
tasks.push(API.sitting.inbox());
}
try {
const results = await Promise.allSettled(tasks);
_sitters = results[0].status === 'fulfilled' ? results[0].value : [];
_mySitter = results[1]?.status === 'fulfilled' ? results[1].value : null;
_myRequests = results[2]?.status === 'fulfilled' ? results[2].value : [];
_inbox = results[3]?.status === 'fulfilled' ? results[3].value : [];
} catch {}
_renderTab();
}
// ----------------------------------------------------------
// Tab-Inhalte
// ----------------------------------------------------------
function _renderTab() {
const content = document.getElementById('sit-content');
if (!content) return;
if (_tab === 'suchen') _renderSuchen(content);
if (_tab === 'profil') _renderProfil(content);
if (_tab === 'anfragen') _renderAnfragen(content);
}
// ---- Tab: Sitter suchen ----
function _renderSuchen(el) {
if (!_sitters.length) {
el.innerHTML = UI.emptyState({ icon: '🐕', title: 'Keine Sitter', text: 'Noch keine Sitter in deiner Nähe registriert.' });
return;
}
el.innerHTML = `
${_sitters.map(s => _sitterCardHTML(s)).join('')}
`;
}
function _sitterCardHTML(s) {
const svcs = (s.services || []).map(id => {
const svc = SERVICES.find(x => x.id === id);
return svc ? `${svc.icon} ${svc.label}` : '';
}).join('');
const dist = s.distanz_m != null
? (s.distanz_m >= 1000 ? (s.distanz_m / 1000).toFixed(1) + ' km' : s.distanz_m + ' m')
: '';
return `
🐾
${UI.escHtml(s.sitter_name)}
${dist ? `
📍 ${dist} entfernt
` : ''}
${s.beschreibung ? `
${UI.escHtml(s.beschreibung)}
` : ''}
${svcs}
${s.preis_pro_tag > 0 ? s.preis_pro_tag.toFixed(0) + ' €/Tag' : 'Preis anfragen'}
max. ${s.max_hunde} Hund${s.max_hunde !== 1 ? 'e' : ''}
`;
}
// ---- Tab: Mein Profil ----
function _renderProfil(el) {
if (!_mySitter) {
el.innerHTML = `
🐾
Werde Hundesitter
Biete anderen Hundebesitzern deine Dienste an und verdiene etwas dazu.
`;
return;
}
const s = _mySitter;
const svcs = (s.services || []).map(id => SERVICES.find(x => x.id === id)?.label || id).join(', ');
el.innerHTML = `
${s.beschreibung ? `
${UI.escHtml(s.beschreibung)}
` : ''}
${s.preis_pro_tag > 0 ? s.preis_pro_tag.toFixed(0) + ' €' : '–'} pro Tag
${s.max_hunde} Hund${s.max_hunde !== 1 ? 'e' : ''} max.
${s.radius_km} km Umkreis
${svcs || '(keine Services angegeben)'}
`;
}
// ---- Tab: Anfragen ----
function _renderAnfragen(el) {
const inbox = _inbox;
const myReqs = _myRequests;
let html = '';
if (inbox.length) {
html += `📬 Eingehende Anfragen (als Sitter)
`;
html += inbox.map(r => _requestCardHTML(r, 'inbox')).join('');
}
if (myReqs.length) {
html += `📤 Meine Anfragen
`;
html += myReqs.map(r => _requestCardHTML(r, 'sent')).join('');
}
if (!inbox.length && !myReqs.length) {
html = UI.emptyState({ icon: '📬', title: 'Keine Anfragen', text: 'Noch keine Sitting-Anfragen vorhanden.' });
}
el.innerHTML = html;
}
function _requestCardHTML(r, mode) {
const STATUS_COLOR = { offen: '#f59e0b', angenommen: '#10b981', abgelehnt: '#ef4444', abgebrochen: '#6b7280' };
const color = STATUS_COLOR[r.status] || '#6b7280';
const name = mode === 'inbox' ? r.anfragender_name : r.sitter_name;
return `
📅 ${r.von} – ${r.bis}
${r.nachricht ? `
${UI.escHtml(r.nachricht)}
` : ''}
${r.status === 'offen' ? _requestActions(r.id, mode) : ''}
`;
}
function _requestActions(id, mode) {
if (mode === 'inbox') {
return `
`;
}
return `
`;
}
// ----------------------------------------------------------
// Sitter Detail + Anfrage senden
// ----------------------------------------------------------
function _showSitterDetail(id) {
const s = _sitters.find(x => x.id === id);
if (!s) return;
const svcs = (s.services || []).map(sv => {
const f = SERVICES.find(x => x.id === sv);
return f ? `${f.icon} ${f.label}` : '';
}).join('');
const dist = s.distanz_m != null
? (s.distanz_m >= 1000 ? (s.distanz_m / 1000).toFixed(1) + ' km' : s.distanz_m + ' m')
: null;
const body = `
🐾
${UI.escHtml(s.sitter_name)}
${dist ? `📍 ${dist} entfernt
` : ''}
${s.beschreibung ? `${UI.escHtml(s.beschreibung)}
` : ''}
${svcs}
${s.preis_pro_tag > 0 ? s.preis_pro_tag.toFixed(0) + ' €' : '–'} pro Tag
${s.max_hunde} max. Hund${s.max_hunde !== 1 ? 'e' : ''}
${s.radius_km} km Umkreis
`;
const footer = _state.user && _mySitter?.user_id !== s.user_id ? `
` : (!_state.user ? `Zum Anfragen bitte einloggen.` : '');
UI.modal.open({ title: 'Sitter-Profil', body, footer });
document.getElementById('sit-anfrage-btn')?.addEventListener('click', () => {
UI.modal.close();
setTimeout(() => _openAnfrageForm(s), 50);
});
}
function _openAnfrageForm(s) {
const dogs = _state.dogs || [];
const id = 'sit-anfrage-form';
const body = `
`;
const footer = `
`;
UI.modal.open({ title: 'Anfrage senden', body, footer });
const form = document.getElementById(id);
const submitBtn = document.querySelector(`[form="${id}"][type="submit"]`) || form.querySelector('[type="submit"]');
form.addEventListener('submit', async e => {
e.preventDefault();
const fd = new FormData(form);
const dogIds = [...form.querySelectorAll('[name="dog_ids"]:checked')].map(cb => parseInt(cb.value));
const data = {
sitter_id: s.id,
von: fd.get('von'),
bis: fd.get('bis'),
dog_ids: dogIds,
nachricht: fd.get('nachricht') || null,
};
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '…'; }
try {
await API.sitting.sendRequest(data);
UI.modal.close();
UI.toast('Anfrage gesendet!');
await _load();
} catch (err) {
UI.toast(err.message, 'error');
} finally {
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Anfrage senden'; }
}
});
}
// ----------------------------------------------------------
// Sitter-Profil Formular
// ----------------------------------------------------------
function _openProfilForm() {
const s = _mySitter;
const id = 'sit-profil-form';
const body = `
`;
const footer = `
`;
UI.modal.open({ title: s ? 'Sitter-Profil bearbeiten' : 'Sitter-Profil erstellen', body, footer });
document.getElementById('sit-gps-btn')?.addEventListener('click', async () => {
try {
const pos = await API.getLocation();
document.getElementById('sit-lat').value = pos.lat.toFixed(6);
document.getElementById('sit-lon').value = pos.lon.toFixed(6);
} catch { UI.toast('GPS nicht verfügbar.', 'error'); }
});
const form = document.getElementById(id);
const submitBtn = document.querySelector(`[form="${id}"][type="submit"]`) || form.querySelector('[type="submit"]');
form.addEventListener('submit', async e => {
e.preventDefault();
const fd = new FormData(form);
const svcs = [...form.querySelectorAll('[name="services"]:checked')].map(cb => cb.value);
const data = {
beschreibung: fd.get('beschreibung') || null,
preis_pro_tag: parseFloat(fd.get('preis_pro_tag')) || 0,
max_hunde: parseInt(fd.get('max_hunde')) || 1,
services: svcs,
lat: fd.get('lat') ? parseFloat(fd.get('lat')) : null,
lon: fd.get('lon') ? parseFloat(fd.get('lon')) : null,
radius_km: parseInt(fd.get('radius_km')) || 20,
};
if (s) data.aktiv = form.querySelector('[name="aktiv"]')?.checked ? 1 : 0;
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = '…'; }
try {
if (s) {
await API.sitting.updateMe(data);
} else {
await API.sitting.create(data);
}
UI.modal.close();
UI.toast(s ? 'Profil aktualisiert.' : 'Profil erstellt!');
await _load();
} catch (err) {
UI.toast(err.message, 'error');
} finally {
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = s ? 'Speichern' : 'Profil erstellen'; }
}
});
}
// ----------------------------------------------------------
// Click-Handler
// ----------------------------------------------------------
function _onClick(e) {
// Tab-Wechsel
const tabBtn = e.target.closest('[data-sit-tab]');
if (tabBtn) {
_tab = tabBtn.dataset.sitTab;
document.querySelectorAll('[data-sit-tab]').forEach(b => b.classList.toggle('active', b.dataset.sitTab === _tab));
_renderTab();
return;
}
// Sitter-Karte
const sitterCard = e.target.closest('[data-sit-id]');
if (sitterCard && !e.target.closest('button')) {
_showSitterDetail(parseInt(sitterCard.dataset.sitId));
return;
}
// Profil erstellen
if (e.target.closest('#sit-create-profil-btn') || e.target.closest('#sit-edit-profil-btn')) {
_openProfilForm();
return;
}
// Anfragen-Aktionen
const acceptBtn = e.target.closest('[data-sit-accept]');
const declineBtn = e.target.closest('[data-sit-decline]');
const cancelBtn = e.target.closest('[data-sit-cancel]');
if (acceptBtn) { _changeReqStatus(parseInt(acceptBtn.dataset.sitAccept), 'angenommen'); return; }
if (declineBtn) { _changeReqStatus(parseInt(declineBtn.dataset.sitDecline), 'abgelehnt'); return; }
if (cancelBtn) { _changeReqStatus(parseInt(cancelBtn.dataset.sitCancel), 'abgebrochen'); return; }
}
async function _changeReqStatus(id, status) {
try {
await API.sitting.updateRequest(id, status);
UI.toast(`Anfrage ${status}.`);
await _load();
} catch (e) { UI.toast(e.message, 'error'); }
}
return { init, refresh };
})();