diff --git a/Makefile b/Makefile index 54d1154..991a0ba 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,7 @@ DS_IP := 10.47.11.10 # Hinweis: NPM braucht 10.47.11.99 als Forward-IP (Macvlan-Shim), nicht .10 DS_SSH_PORT := 22 DS_PATH := /volume1/docker/ban-yaro -CONTAINER := ban-yaro # container_name (für docker logs/exec) -SERVICE := banyaro # service-name in docker-compose.yml (für docker compose restart) +CONTAINER := ban-yaro GIT_REMOTE := origin DOCKER := sudo /usr/local/bin/docker @@ -108,7 +107,7 @@ push: restart: check-ssh @ssh $(DS_HOST) " \ cd $(DS_PATH) && \ - $(DOCKER) compose restart $(SERVICE)" + $(DOCKER) compose restart $(CONTAINER)" @echo " ✓ Neugestartet." # ---------------------------------------------------------- diff --git a/backend/static/js/app.js b/backend/static/js/app.js index ba2ab09..1431cd6 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -214,7 +214,8 @@ const App = (() => { function _onLoggedOut() { state.user = null; - navigate('settings', false); + // Zeige Login wenn nötig + // Für MVP: direkte Weiterleitung zu Einstellungen/Login } async function _loadDogs() { diff --git a/backend/static/js/pages/dog-profile.js b/backend/static/js/pages/dog-profile.js deleted file mode 100644 index 9611520..0000000 --- a/backend/static/js/pages/dog-profile.js +++ /dev/null @@ -1,395 +0,0 @@ -/* ============================================================ - BAN YARO — Hunde-Profil - Seiten-Modul: Profil anlegen / anzeigen / bearbeiten. - ============================================================ */ - -window.Page_dog_profile = (() => { - - let _container = null; - let _appState = null; - - // ---------------------------------------------------------- - // INIT / REFRESH / LIFECYCLE - // ---------------------------------------------------------- - async function init(container, appState) { - _container = container; - _appState = appState; - await _render(); - } - - async function refresh() { - await _render(); - } - - async function onDogChange(dog) { - await _render(); - } - - // ---------------------------------------------------------- - // HAUPTRENDER - // ---------------------------------------------------------- - async function _render() { - if (!_appState.user) { - _container.innerHTML = UI.emptyState({ - icon : '🐕', - title : 'Anmelden erforderlich', - text : 'Melde dich an, um ein Hundeprofil anzulegen.', - action: ``, - }); - _container.querySelector('#profile-goto-login') - ?.addEventListener('click', () => App.navigate('settings')); - return; - } - - if (!_appState.activeDog) { - _renderCreateForm(); - } else { - _renderProfile(_appState.activeDog); - } - } - - // ---------------------------------------------------------- - // PROFIL-ANSICHT - // ---------------------------------------------------------- - function _renderProfile(dog) { - const geburtstag = dog.geburtstag - ? new Date(dog.geburtstag + 'T00:00:00') - .toLocaleDateString('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }) - : null; - - _container.innerHTML = ` -
- - -
- ${dog.foto_url - ? `${_esc(dog.name)}` - : `
🐕
`} - -
- - -

${_esc(dog.name)}

- ${dog.rasse - ? `

${_esc(dog.rasse)}

` - : `

`} - - -
- ${geburtstag ? ` -
-
🎂 Geburtstag
-
${geburtstag}
-
- ${_calcAlter(dog.geburtstag)} -
-
- ` : ''} - ${dog.geschlecht ? ` -
-
${dog.geschlecht === 'm' ? '♂' : '♀'} Geschlecht
-
- ${dog.geschlecht === 'm' ? 'Rüde' : 'Hündin'} -
-
- ` : ''} - ${dog.gewicht_kg ? ` -
-
⚖️ Gewicht
-
${dog.gewicht_kg} kg
-
- ` : ''} - ${dog.chip_nr ? ` -
-
💾 Chip-Nr.
-
${_esc(dog.chip_nr)}
-
- ` : ''} -
- - ${dog.bio ? ` -
-

- "${_esc(dog.bio)}" -

-
- ` : ''} - - - -
- `; - - // Foto hochladen - document.getElementById('dp-photo-input')?.addEventListener('change', async e => { - const file = e.target.files[0]; - if (!file) return; - try { - const fd = new FormData(); - fd.append('file', file); - const result = await API.dogs.uploadPhoto(dog.id, fd); - // State in-place aktualisieren - dog.foto_url = result.foto_url; - _appState.activeDog = { ..._appState.activeDog, foto_url: result.foto_url }; - _appState.dogs = _appState.dogs.map(d => - d.id === dog.id ? _appState.activeDog : d - ); - UI.toast.success('Foto gespeichert.'); - _renderProfile(_appState.activeDog); - } catch (err) { - UI.toast.error(err.message || 'Fehler beim Hochladen.'); - } - }); - - // Bearbeiten öffnen - document.getElementById('dp-edit-btn')?.addEventListener('click', () => { - _openEditModal(dog); - }); - } - - // ---------------------------------------------------------- - // NEU ANLEGEN (direkt auf der Seite, kein Modal) - // ---------------------------------------------------------- - function _renderCreateForm() { - _container.innerHTML = ` -
-
-
🐕
-

- Hund anlegen -

-

- Erstelle das Profil für deinen Hund. -

-
- ${_formHTML(null)} -
- `; - _bindForm(null, false); - } - - // ---------------------------------------------------------- - // BEARBEITEN (Modal) - // ---------------------------------------------------------- - function _openEditModal(dog) { - UI.modal.open({ title: `${dog.name} bearbeiten`, body: _formHTML(dog) }); - _bindForm(dog, true); - } - - // ---------------------------------------------------------- - // FORMULAR HTML - // ---------------------------------------------------------- - function _formHTML(dog) { - const today = new Date().toISOString().slice(0, 10); - return ` -
- -
- - -
- -
- - -
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
- - -
- -
- -
- -
- ${dog ? `` : ''} - -
- - ${dog ? ` -
- -
- ` : ''} - -
- `; - } - - // ---------------------------------------------------------- - // FORMULAR EVENTS - // ---------------------------------------------------------- - function _bindForm(dog, inModal) { - const form = document.getElementById('dp-form'); - if (!form) return; - - document.getElementById('dp-form-cancel') - ?.addEventListener('click', UI.modal.close); - - document.getElementById('dp-delete-btn')?.addEventListener('click', async () => { - const ok = await UI.modal.confirm({ - title : `${dog.name} löschen?`, - message: 'Tagebuch-Einträge und Gesundheitsdaten werden ebenfalls gelöscht. Nicht rückgängig.', - confirmText: 'Löschen', - danger : true, - }); - if (!ok) return; - try { - await API.dogs.delete(dog.id); - _appState.dogs = _appState.dogs.filter(d => d.id !== dog.id); - _appState.activeDog = _appState.dogs[0] || null; - if (inModal) UI.modal.close(); - UI.toast.success(`${dog.name} wurde gelöscht.`); - await _render(); - } catch (err) { - UI.toast.error(err.message || 'Fehler beim Löschen.'); - } - }); - - form.addEventListener('submit', async e => { - e.preventDefault(); - const btn = form.querySelector('[type="submit"]'); - const fd = UI.formData(form); - - if (!fd.name?.trim()) { - UI.toast.warning('Bitte einen Namen eingeben.'); - return; - } - - await UI.asyncButton(btn, async () => { - const payload = { - name: fd.name.trim(), - rasse: fd.rasse || null, - geburtstag: fd.geburtstag || null, - geschlecht: fd.geschlecht || null, - gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null, - chip_nr: fd.chip_nr || null, - bio: fd.bio || null, - is_public: 'is_public' in fd, - }; - - let saved; - if (dog) { - saved = await API.dogs.update(dog.id, payload); - _appState.dogs = _appState.dogs.map(d => d.id === dog.id ? saved : d); - _appState.activeDog = saved; - if (inModal) UI.modal.close(); - UI.toast.success('Profil gespeichert.'); - } else { - saved = await API.dogs.create(payload); - _appState.dogs.push(saved); - _appState.activeDog = saved; - UI.toast.success(`${saved.name} wurde angelegt! 🎉`); - } - await _render(); - }); - }); - } - - // ---------------------------------------------------------- - // HELPER - // ---------------------------------------------------------- - function _calcAlter(geburtstag) { - const born = new Date(geburtstag + 'T00:00:00'); - const tage = Math.floor((Date.now() - born) / 86400000); - if (tage < 0) return ''; - if (tage < 30) return `${tage} Tag${tage !== 1 ? 'e' : ''} alt`; - if (tage < 365) { - const m = Math.floor(tage / 30); - return `${m} Monat${m !== 1 ? 'e' : ''} alt`; - } - const j = Math.floor(tage / 365); - const m = Math.floor((tage % 365) / 30); - return m > 0 - ? `${j} Jahr${j !== 1 ? 'e' : ''}, ${m} Monat${m !== 1 ? 'e' : ''} alt` - : `${j} Jahr${j !== 1 ? 'e' : ''} alt`; - } - - function _esc(str) { - if (!str) return ''; - return str.replace(/&/g, '&').replace(//g, '>') - .replace(/"/g, '"'); - } - - // ---------------------------------------------------------- - // PUBLIC - // ---------------------------------------------------------- - return { init, refresh, onDogChange }; - -})(); diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js deleted file mode 100644 index 345d162..0000000 --- a/backend/static/js/pages/settings.js +++ /dev/null @@ -1,306 +0,0 @@ -/* ============================================================ - BAN YARO — Einstellungen / Account - Login, Registrierung, Logout, Account-Info. - ============================================================ */ - -window.Page_settings = (() => { - - let _container = null; - let _appState = null; - let _mode = 'login'; // 'login' | 'register' - - // ---------------------------------------------------------- - // INIT / REFRESH - // ---------------------------------------------------------- - async function init(container, appState) { - _container = container; - _appState = appState; - _render(); - } - - function refresh() { - _render(); - } - - // ---------------------------------------------------------- - // RENDER - // ---------------------------------------------------------- - function _render() { - if (_appState.user) { - _renderAccount(); - } else { - _renderAuth(_mode); - } - } - - // ---------------------------------------------------------- - // EINGELOGGT — Account-Übersicht - // ---------------------------------------------------------- - function _renderAccount() { - const u = _appState.user; - _container.innerHTML = ` -
- -
-
-
- ${_esc(u.name.charAt(0).toUpperCase())} -
-
-
${_esc(u.name)}
-
${_esc(u.email)}
- ${u.is_premium - ? ` - ⭐ Ban Yaro Plus - ` - : ` - Kostenlos - `} -
-
-
- -
-
- - - -
-
- -
- Ban Yaro · banyaro.app
- Deine Daten liegen auf einem eigenen Server in Deutschland. -
- -
- `; - - document.getElementById('settings-logout-btn')?.addEventListener('click', async () => { - const ok = await UI.modal.confirm({ - title : 'Abmelden?', - message: 'Du wirst aus deinem Konto abgemeldet.', - confirmText: 'Abmelden', - }); - if (!ok) return; - try { - await API.auth.logout(); - } catch { /* cookie wird trotzdem gelöscht */ } - _appState.user = null; - _appState.dogs = []; - _appState.activeDog = null; - UI.toast.info('Du wurdest abgemeldet.'); - _render(); - }); - - document.getElementById('settings-push-btn')?.addEventListener('click', async () => { - try { - await API.subscribeToPush(); - UI.toast.success('Push-Benachrichtigungen aktiviert.'); - } catch { - UI.toast.warning('Push-Benachrichtigungen konnten nicht aktiviert werden.'); - } - }); - } - - // ---------------------------------------------------------- - // NICHT EINGELOGGT — Login / Registrierung - // ---------------------------------------------------------- - function _renderAuth(mode) { - _mode = mode; - _container.innerHTML = ` -
- - -
- Ban Yaro -

Ban Yaro

-

- Alles rund um deinen Hund -

-
- - -
- - -
- - ${mode === 'login' ? _loginFormHTML() : _registerFormHTML()} - -
- `; - - document.getElementById('tab-login') - ?.addEventListener('click', () => _renderAuth('login')); - document.getElementById('tab-register') - ?.addEventListener('click', () => _renderAuth('register')); - - if (mode === 'login') { - _bindLoginForm(); - } else { - _bindRegisterForm(); - } - } - - function _loginFormHTML() { - return ` -
-
- - -
-
- - -
- -
- `; - } - - function _registerFormHTML() { - return ` -
-
- - -
-
- - -
-
- - -
- -

- Mit der Registrierung stimmst du unseren Datenschutzhinweisen zu.
- Deine Daten werden ausschließlich auf unserem Server gespeichert. -

-
- `; - } - - function _bindLoginForm() { - document.getElementById('auth-form')?.addEventListener('submit', async e => { - e.preventDefault(); - const btn = e.target.querySelector('[type="submit"]'); - const fd = UI.formData(e.target); - - await UI.asyncButton(btn, async () => { - const result = await API.auth.login(fd.email, fd.password); - localStorage.setItem('by_token', result.token); - - // User-Daten laden - _appState.user = await API.auth.me(); - document.getElementById('sidebar-username').textContent = _appState.user.name; - - // Hunde laden - try { - _appState.dogs = await API.dogs.list(); - _appState.activeDog = _appState.dogs[0] || null; - } catch { /* keine Hunde = okay */ } - - UI.toast.success(`Willkommen zurück, ${_appState.user.name}!`); - - // Nach Login: Tagebuch oder Profil anlegen - if (_appState.activeDog) { - App.navigate('diary'); - } else { - App.navigate('dog-profile'); - } - }); - }); - } - - function _bindRegisterForm() { - document.getElementById('auth-form')?.addEventListener('submit', async e => { - e.preventDefault(); - const btn = e.target.querySelector('[type="submit"]'); - const fd = UI.formData(e.target); - - if (!fd.name?.trim()) { - UI.toast.warning('Bitte einen Namen eingeben.'); - return; - } - if ((fd.password || '').length < 8) { - UI.toast.warning('Passwort muss mindestens 8 Zeichen lang sein.'); - return; - } - - await UI.asyncButton(btn, async () => { - const result = await API.auth.register(fd.email, fd.password, fd.name.trim()); - localStorage.setItem('by_token', result.token); - - _appState.user = await API.auth.me(); - document.getElementById('sidebar-username').textContent = _appState.user.name; - _appState.dogs = []; - _appState.activeDog = null; - - UI.toast.success(`Willkommen bei Ban Yaro, ${_appState.user.name}! 🐕`); - // Direkt zur Profil-Anlage - App.navigate('dog-profile'); - }); - }); - } - - // ---------------------------------------------------------- - // HELPER - // ---------------------------------------------------------- - function _esc(str) { - if (!str) return ''; - return str.replace(/&/g, '&').replace(//g, '>') - .replace(/"/g, '"'); - } - - // ---------------------------------------------------------- - // PUBLIC - // ---------------------------------------------------------- - return { init, refresh }; - -})();