+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/calendar.js b/public/pages/calendar.js
new file mode 100644
index 0000000..46fc333
--- /dev/null
+++ b/public/pages/calendar.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Calendar
+ * Zweck: Seite für das Calendar-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/contacts.js b/public/pages/contacts.js
new file mode 100644
index 0000000..cb218ea
--- /dev/null
+++ b/public/pages/contacts.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Contacts
+ * Zweck: Seite für das Contacts-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js
new file mode 100644
index 0000000..3795c3a
--- /dev/null
+++ b/public/pages/dashboard.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Dashboard
+ * Zweck: Seite für das Dashboard-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/login.js b/public/pages/login.js
new file mode 100644
index 0000000..31899c2
--- /dev/null
+++ b/public/pages/login.js
@@ -0,0 +1,96 @@
+/**
+ * Modul: Login-Seite
+ * Zweck: Anmeldeformular mit Username/Passwort, Fehlerbehandlung, Session-Start
+ * Abhängigkeiten: /api.js
+ */
+
+import { auth } from '/api.js';
+
+/**
+ * Rendert die Login-Seite in den gegebenen Container.
+ * @param {HTMLElement} container
+ */
+export async function render(container) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/notes.js b/public/pages/notes.js
new file mode 100644
index 0000000..f58bff4
--- /dev/null
+++ b/public/pages/notes.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Notes
+ * Zweck: Seite für das Notes-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/settings.js b/public/pages/settings.js
new file mode 100644
index 0000000..ebcac98
--- /dev/null
+++ b/public/pages/settings.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Settings
+ * Zweck: Seite für das Settings-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/shopping.js b/public/pages/shopping.js
new file mode 100644
index 0000000..9929066
--- /dev/null
+++ b/public/pages/shopping.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Shopping
+ * Zweck: Seite für das Shopping-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/pages/tasks.js b/public/pages/tasks.js
new file mode 100644
index 0000000..5a62473
--- /dev/null
+++ b/public/pages/tasks.js
@@ -0,0 +1,25 @@
+/**
+ * Modul: Tasks
+ * Zweck: Seite für das Tasks-Modul
+ * Abhängigkeiten: /api.js
+ */
+
+import { api } from '/api.js';
+
+/**
+ * @param {HTMLElement} container
+ * @param {{ user: object }} context
+ */
+export async function render(container, { user }) {
+ container.innerHTML = `
+
+
+
+
Kommt bald.
+
Dieses Modul wird in Phase 2 implementiert.
+
+
+ `;
+}
diff --git a/public/router.js b/public/router.js
new file mode 100644
index 0000000..7597707
--- /dev/null
+++ b/public/router.js
@@ -0,0 +1,237 @@
+/**
+ * Modul: Client-Side Router
+ * Zweck: SPA-Routing über History API ohne Framework, Auth-Guard, Seiten-Übergänge
+ * Abhängigkeiten: api.js
+ */
+
+import { auth } from '/api.js';
+
+// --------------------------------------------------------
+// Routen-Definitionen
+// Jede Route hat: path, page (dynamisch geladen), requiresAuth
+// --------------------------------------------------------
+const ROUTES = [
+ { path: '/login', page: '/pages/login.js', requiresAuth: false },
+ { path: '/', page: '/pages/dashboard.js', requiresAuth: true },
+ { path: '/tasks', page: '/pages/tasks.js', requiresAuth: true },
+ { path: '/shopping', page: '/pages/shopping.js', requiresAuth: true },
+ { path: '/meals', page: '/pages/meals.js', requiresAuth: true },
+ { path: '/calendar', page: '/pages/calendar.js', requiresAuth: true },
+ { path: '/notes', page: '/pages/notes.js', requiresAuth: true },
+ { path: '/contacts', page: '/pages/contacts.js', requiresAuth: true },
+ { path: '/budget', page: '/pages/budget.js', requiresAuth: true },
+ { path: '/settings', page: '/pages/settings.js', requiresAuth: true },
+];
+
+// --------------------------------------------------------
+// Globaler App-State
+// --------------------------------------------------------
+let currentUser = null;
+let currentPath = null;
+
+// --------------------------------------------------------
+// Router
+// --------------------------------------------------------
+
+/**
+ * Navigiert zu einem Pfad und rendert die entsprechende Seite.
+ * @param {string} path
+ * @param {boolean} pushState - false beim initialen Load und popstate
+ */
+async function navigate(path, pushState = true) {
+ if (path === currentPath) return;
+ currentPath = path;
+
+ const route = ROUTES.find((r) => r.path === path) ?? ROUTES.find((r) => r.path === '/');
+
+ // Auth-Guard
+ if (route.requiresAuth && !currentUser) {
+ try {
+ const result = await auth.me();
+ currentUser = result.user;
+ } catch {
+ navigateTo('/login', true);
+ return;
+ }
+ }
+
+ if (!route.requiresAuth && currentUser && path === '/login') {
+ navigateTo('/', true);
+ return;
+ }
+
+ if (pushState) {
+ history.pushState({ path }, '', path);
+ }
+
+ await renderPage(route);
+ updateNav(path);
+}
+
+/**
+ * Lädt und rendert eine Seite dynamisch.
+ * @param {{ path: string, page: string }} route
+ */
+async function renderPage(route) {
+ const app = document.getElementById('app');
+ const loading = document.getElementById('app-loading');
+
+ // Loading verstecken
+ if (loading) loading.hidden = true;
+
+ try {
+ const module = await import(route.page + '?v=1');
+
+ if (typeof module.render !== 'function') {
+ throw new Error(`Seite ${route.page} exportiert keine render()-Funktion.`);
+ }
+
+ // Seiten-Wrapper erstellen
+ const pageWrapper = document.createElement('div');
+ pageWrapper.className = 'page-transition';
+ pageWrapper.style.animation = 'page-in 0.2s ease forwards';
+
+ await module.render(pageWrapper, { user: currentUser });
+
+ // Nav + Content einmalig aufbauen (beim ersten Render)
+ if (!document.querySelector('.nav-bottom') && currentUser) {
+ renderAppShell(app);
+ }
+
+ const content = document.getElementById('page-content') || app;
+ content.replaceChildren(pageWrapper);
+
+ } catch (err) {
+ console.error('[Router] Seiten-Render-Fehler:', err);
+ renderError(app, err);
+ }
+}
+
+/**
+ * App-Shell mit Navigation einmalig aufbauen (nach erstem Login).
+ */
+function renderAppShell(container) {
+ container.innerHTML = `
+
+
+