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)}
- ${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 `
-
- `;
- }
-
- // ----------------------------------------------------------
- // 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
-
- 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 `
-
- `;
- }
-
- 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 };
-
-})();