diff --git a/README.md b/README.md index 15b7c59..d1025c3 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,20 @@ -# Hocks Mieterportal +# Hinterstein Immobilien · Mieterportal -Mieterportal der Hocks Verwaltung für **50 Wohnungen in 3 Häusern** (Hauptstraße 12 · +Mieterportal der **Hinterstein Immobilien** für **50 Wohnungen in 3 Häusern** (Hauptstraße 12 · Ringweg 3 · Am Park 5) – analog zum [Schulportal](https://git.hocks.eu/ah-admin/Schulportal). Live-Ziel: `https://mieterportal.hocks.eu`. ## Module 1. **Dashboard** – persönliche Übersicht (Mieter) bzw. Kennzahlen, Belegung und Einnahmen (Verwaltung) -2. **Mietkonto & Zahlungen** – Buchungsverlauf, offene Forderungen, Nebenkosten; Verwaltung sieht Zahlungsstatus aller 50 Wohnungen -3. **Schadensmeldungen** – Tickets erfassen, priorisieren und Status pflegen -4. **Dokumente** – Verträge, Abrechnungen, Hausordnung; zentraler Such- und Filterzugriff -5. **Nachrichten** – Direktkommunikation Mieter ↔ Verwaltung +2. **Benutzerverwaltung** (nur Verwaltung) – Konten anlegen/pflegen, Registrierungscodes erzeugen +3. **Mietkonto & Zahlungen** – Buchungsverlauf, offene Forderungen, Nebenkosten; Verwaltung sieht Zahlungsstatus aller 50 Wohnungen +4. **Schadensmeldungen** – Tickets erfassen, priorisieren und Status pflegen +5. **Dokumente** – Verträge, Abrechnungen, Hausordnung; zentraler Such- und Filterzugriff +6. **Nachrichten** – Direktkommunikation Mieter ↔ Verwaltung -Der Rollenwechsel Mieter ↔ Verwaltung erfolgt über die Seitenleiste (Demo-Modus). +**Anmeldung erforderlich** – Gruppen: **Verwaltung** und **Mieter** (Registrierung der Mieter per Code auf der Startseite). +Demo-Zugänge (Passwort jeweils `0815`): `j.hinterstein` (Verwaltung) · `a.berger` (Mieterin) · Demo-Registrierungscode: `MP-2026-0815`. Alle Demo-Daten werden lokal im Browser (localStorage) gespeichert. Dark-Mode-Umschalter in der Seitenleiste bzw. Mobilen Kopfzeile. diff --git a/index.html b/index.html index fa208e9..ad0b398 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Hocks Mieterportal + Hinterstein Immobilien · Mieterportal
diff --git a/kimi.md b/kimi.md index 595d239..cc991fb 100644 --- a/kimi.md +++ b/kimi.md @@ -1,17 +1,26 @@ -# Kimi-Kontext: Hocks Mieterportal (mieterportal.hocks.eu) +# Kimi-Kontext: Hinterstein Immobilien · Mieterportal (mieterportal.hocks.eu) -> Projekt-Kontextdatei für Kimi Work. Stand: 02.10.2026, 11:00 Uhr. +> Projekt-Kontextdatei für Kimi Work. Stand: 02.10.2026, 14:30 Uhr. > Bitte bei größeren Änderungen am Projekt aktualisieren. > Konventionen und Workflow sind am Projekt Schulportal (sfg.hocks.eu) orientiert. ## 1. Projektüberblick -Mieterportal der Hocks Verwaltung für **50 Wohnungen in 3 Häusern** (Hauptstraße 12 · -Ringweg 3 · Am Park 5). Analog zum Schulportal: alle Mietangelegenheiten digital an -einem Ort. **Rollenmodus statt echter Anmeldung** (Demo): Umschalten Mieter ↔ -Verwaltung über die Seitenleiste (Schaltfläche „Als Verwaltung anzeigen"). +Mieterportal der **Hinterstein Immobilien** (Demo, Inhaber Joseph Hinterstein) für +**50 Wohnungen in 3 Häusern** (Hauptstraße 12 · Ringweg 3 · Am Park 5). Analog zum +Schulportal: alle Mietangelegenheiten digital an einem Ort. -**Die 5 Module:** +**Authentifizierung (echtes Login seit v1.1):** Startseite `/login` mit +Login-Pflicht (Route-Guard `RequireAuth` in `App.tsx` leitet unangemeldete Besucher +auf `/login` um). **Gruppen:** `verwaltung` und `mieter`. Mieter können sich über +einen **Registrierungscode** selbst registrieren (`/registrieren`); Codes legt die +Verwaltung in der Benutzerverwaltung an (einmalig nutzbar, Wohnungsbezug). +Passwörter als **SHA-256-Hash** in localStorage, Session 8 h +(Key `mp_benutzer_session`). Demo-Zugänge (Passwort `0815`): +`j.hinterstein` (Verwaltung, Joseph Hinterstein), `a.berger` (Mieterin, +Hauptstraße 12 Whg. 7), `m.wolf` (Mieter). Demo-Registrierungscode: `MP-2026-0815`. + +**Die 6 Module:** 1. **Dashboard** (`/`) – Mieter: Begrüßung, nächste Zahlung, Mietdiagramm, Ankündigungen, offene Meldungen. Verwaltung: Kennzahlen (Vermietungsquote, Miet-Soll, Rückstände), @@ -25,21 +34,21 @@ Verwaltung über die Seitenleiste (Schaltfläche „Als Verwaltung anzeigen"). Suche + Kategorie-Chips. Mieter sieht eigene + Hausdokumente (unitId=null). 5. **Nachrichten** (`/nachrichten`) – Chat Mieter ↔ Verwaltung, Ungelesen-Markierung, neue Unterhaltungen. - -**Login-Seite** (`/login`): Hero mit generiertem Foto, Feature-Liste, Demo-Einstiege -(„Als Mieterin anmelden" / „Als Verwaltung anmelden"). Kein echtes Auth – der -Rollenwechsel setzt nur den Store. +6. **Benutzerverwaltung** (`/benutzer`, nur Gruppe `verwaltung`) – Nutzerliste mit + Gruppen und Wohnungsbezug, Registrierungscodes erzeugen/deaktivieren, + Demo-Zugänge zurücksetzen. ## 2. Technischer Stack | Teil | Technologie | |---|---| | Frontend | React 18 + TypeScript, Vite 7, Tailwind v3.4 + shadcn/ui (Radix), React Router 7 (BrowserRouter), Recharts | -| State | React Context + useReducer, Persistenz in localStorage (`hocks-mieterportal-v1`) | +| State | React Context + useReducer, Persistenz in localStorage (`hinterstein-mieterportal-v2`) | | Daten | `src/data/seed.ts` (handschriftlich) + `src/data/generated.json` (maschinell via `beispieldaten.py`) | -| Branding | KI-generiert: `src/assets/logo.png` (Haus-Schlüssel-Emblem, transparent), `hero.jpg`, `modules.jpg` | +| Auth | `src/lib/auth.ts` (SHA-256, Session 8 h, Keys `mp_benutzer`/`mp_benutzer_session`), Route-Guard in `App.tsx` | +| Branding | Kunde: `src/assets/hinterstein/` (logo.png, joseph-hinterstein.jpg, kontakt.jpg); KI-generiert: `src/assets/logo.png`, `hero.jpg`, `modules.jpg` | | Webserver (Ziel) | Docker-Container `mp-portal` (nginx), statisches `dist/`, gemountet `/opt/mieterportal/dist` | -| Mail (optional) | `mail-server/`: Postfix-Relay (`mp-mail`) + Python-API (`mp-mail-api`), Muster aus Schulportal | +| Mail | `mail-server/`: Postfix-Relay (`mp-mail`) + Python-API (`mp-mail-api`) – **live auf dem Server** unter `/opt/mp-mail` | | Server | Root-VPS hocks.eu (IONOS), Docker + Portainer, Subdomain via NPM (Nginx Proxy Manager, Let's Encrypt) | ## 3. Wichtige Dateien @@ -58,7 +67,8 @@ ausgesperrt via `.gitignore` (`mail-server/mail.env` etc.). | `src/components/AppLayout.tsx` | Sidebar (Desktop) + Mobile Top-Bar/Nav, Rollenwechsel, Ungelesen-Badge, ThemeToggle | | `src/components/ThemeToggle.tsx` | Dark-Mode-Umschalter (Sonne/Mond) | | `src/lib/theme.ts` | `mp-theme` in localStorage, Init vor erstem Render (main.tsx) | -| `src/pages/Login.tsx` | Landing/Login mit Hero-Bild und Demo-Einstiegen | +| `src/pages/Login.tsx` | Login-Pflicht: Hero mit Joseph-Hinterstein-Portrait, Logo, Feature-Liste, Login-Formular + Registrierung-Link + Demo-Einstiege | +| `src/pages/Benutzer.tsx` | Benutzerverwaltung (nur Verwaltung): Nutzer, Gruppen, Registrierungscodes | | `Dockerfile` | Optionaler All-in-One-Build (node build → nginx) für `docker build`-Workflow | | `docker/nginx.conf` | nginx-Site: SPA-Fallback, Gzip, Cache-Header für /assets/, Security-Header | | `docker-compose.yml` | `mp-portal` (nginx + gemountetes dist) im externen Netz `npm_default`; Mail-Stack separat | @@ -106,11 +116,17 @@ tar -czf mp-dist.tar.gz -C dist . ## 5. Zugänge & Konfiguration -- Keine echten Zugänge (Demo). Rollenwechsel im UI; Store-Reset über Browser-DevTools - (localStorage-Key `hocks-mieterportal-v1` löschen). -- Demo-Mieterin: **Anna Berger**, Hauptstraße 12, Whg. 7 (id `hs-7`). -- **Mail-API-Token:** bewusst nicht hier dokumentiert – liegt in `mail-server/mail.env` - (nicht im Repo; Vorlage `mail.env.example`). +- **Demo-Zugänge (Passwort `0815`):** `j.hinterstein` (Verwaltung), `a.berger` + (Mieterin, Hauptstraße 12 Whg. 7), `m.wolf` (Mieter). Demo-Registrierungscode: + `MP-2026-0815`. Passwörter werden als SHA-256-Hash gespeichert; Store-Reset über + Browser-DevTools (localStorage-Keys `mp_benutzer`, `mp_benutzer_session`, + `hinterstein-mieterportal-v2` löschen). +- **Mail-API-Token:** bewusst nicht hier dokumentiert – liegt auf dem Server in + `/opt/mp-mail/mail.env` (Vorlage `mail.env.example` im Repo). +- **Mail-Stack live (seit 02.10.2026):** Container `mp-mail` + `mp-mail-api` laufen + auf hocks.eu unter `/opt/mp-mail` (Relay über `hocks-eu.mail.protection.outlook.com`). + Testmail und API-`/send` (mit `X-Api-Token`) verifiziert (status=sent); ohne Token 401. + Noch nicht mit dem Portal-Frontend verkabelt (keine automatischen Benachrichtigungen). ## 6. Konventionen & Besonderheiten @@ -149,13 +165,22 @@ bis 31.12.2026. Zertifikatsdateien liegen im npm-Container unter `/etc/letsencry (Backup der Zertifikate auf dem Host: `/root/le-mp/`). Fail2Ban-Ban nach frühen Fehlversuchen ist nach ~40 min automatisch ausgelaufen. +**Erledigt 02.10. (v1.1 live):** Rebrand auf „Hinterstein Immobilien" (Kunden-Logo, +Joseph-Hinterstein-Portrait auf der Login-Seite, Kontakt-Banner), echtes +Authentifizierungssystem (Login-Pflicht, Gruppen Verwaltung/Mieter, SHA-256, +Session 8 h), Mieter-Registrierung per einmaligem Code, Benutzerverwaltung als +6. Modul (`/benutzer`), Demo-Zugänge mit Passwort `0815`. Mail-Stack auf dem Server +aktiviert (`/opt/mp-mail`, Testmail + API-/send verifiziert). Store-Key auf +`hinterstein-mieterportal-v2` gehoben. Live deployed und geprüft (HTTP 200, Titel +„Hinterstein Immobilien · Mieterportal"). + **Offen:** - Zertifikatserneuerung prüfen (per 01.12.2026): NPM-Renewer sollte cert 9 über HTTP-Challenge erneuern; sonst certbot/webroot + Kopie nach npm-9 wiederholen. -- Mail-Stack optional einrichten: `mail.env` aus Vorlage befüllen, Relay-Ziel in - `postfix/main.cf`/`mail.env` setzen, `docker compose up -d --build` im mail-server-Verzeichnis. -- Mögliche nächste Schritte: echte Anmeldung + serverseitige Datenbank (PostgreSQL/Node-API), - E-Mail-Benachrichtigung bei neuen Schadensmeldungen, Mehrseitigkeit für 50 echte Mietverhältnisse. +- Portal-Frontend an Mail-API verkabeln: automatische E-Mail an die Verwaltung bei neuen + Schadensmeldungen und an Mieter bei Antworten. +- Mögliche nächste Schritte: serverseitige Datenbank (PostgreSQL/Node-API) statt localStorage, + Mehrseitigkeit für 50 echte Mietverhältnisse. ## 9. Sonstiges diff --git a/mail-server/api/server.py b/mail-server/api/server.py index a93490b..c78fdea 100644 --- a/mail-server/api/server.py +++ b/mail-server/api/server.py @@ -33,7 +33,7 @@ PORT = int(os.environ.get("PORT", "8080")) ALLOWED_ORIGIN = os.environ.get("ALLOWED_ORIGIN", "*") DATA_DIR = os.environ.get("DATA_DIR", "/data") SENDER_ADDRESS = os.environ.get("SENDER_ADDRESS", "noreply@hocks.eu") -SENDER_NAME = os.environ.get("SENDER_NAME", "Hocks Mieterportal") +SENDER_NAME = os.environ.get("SENDER_NAME", "Hinterstein Immobilien") MAIL_API_TOKEN = os.environ.get("MAIL_API_TOKEN", "") RATE_LIMIT = int(os.environ.get("RATE_LIMIT", "500")) # Mails pro Stunde diff --git a/preview/06-login-hinterstein.png b/preview/06-login-hinterstein.png new file mode 100644 index 0000000..56c790b Binary files /dev/null and b/preview/06-login-hinterstein.png differ diff --git a/public/logo.png b/public/logo.png index 50e568e..439b4a6 100644 Binary files a/public/logo.png and b/public/logo.png differ diff --git a/src/App.tsx b/src/App.tsx index eb5212b..531f0a4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { Routes, Route } from 'react-router' -import { StoreProvider } from './store/store' +import { Routes, Route, Navigate } from 'react-router' +import { StoreProvider, useStore } from './store/store' import AppLayout from './components/AppLayout' import Login from './pages/Login' import Dashboard from './pages/Dashboard' @@ -7,19 +7,40 @@ import Payments from './pages/Payments' import Tickets from './pages/Tickets' import Documents from './pages/Documents' import Messages from './pages/Messages' +import BenutzerVerwaltung from './pages/BenutzerVerwaltung' + +function RequireAuth({ children }: { children: React.ReactElement }) { + const { me } = useStore() + if (!me) return + return children +} + +function LoginGate() { + const { me } = useStore() + if (me) return + return +} export default function App() { return ( - } /> - }> + } /> + + + + } + > } /> } /> } /> } /> } /> + } /> + } /> ) diff --git a/src/assets/hinterstein/joseph-hinterstein.jpg b/src/assets/hinterstein/joseph-hinterstein.jpg new file mode 100644 index 0000000..2065d95 Binary files /dev/null and b/src/assets/hinterstein/joseph-hinterstein.jpg differ diff --git a/src/assets/hinterstein/kontakt.jpg b/src/assets/hinterstein/kontakt.jpg new file mode 100644 index 0000000..24df0fb Binary files /dev/null and b/src/assets/hinterstein/kontakt.jpg differ diff --git a/src/assets/hinterstein/logo.png b/src/assets/hinterstein/logo.png new file mode 100644 index 0000000..439b4a6 Binary files /dev/null and b/src/assets/hinterstein/logo.png differ diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx index 18298bf..3c46629 100644 --- a/src/components/AppLayout.tsx +++ b/src/components/AppLayout.tsx @@ -1,17 +1,17 @@ -import { NavLink, Outlet } from 'react-router' +import { NavLink, Outlet, useNavigate } from 'react-router' import { LayoutDashboard, Wallet, Wrench, FileText, MessagesSquare, + UserCog, LogOut, Building2, - Bell, } from 'lucide-react' import { useStore } from '../store/store' import ThemeToggle from './ThemeToggle' -import logo from '../assets/logo.png' +import logo from '../assets/hinterstein/logo.png' const navItems = [ { to: '/', label: 'Dashboard', icon: LayoutDashboard, end: true }, @@ -22,44 +22,46 @@ const navItems = [ ] export default function AppLayout() { - const { role, dispatch, threads } = useStore() + const { role, me, logout, threads } = useStore() + const navigate = useNavigate() const unread = threads.filter((t) => role === 'tenant' ? t.unreadByTenant : t.unreadByAdmin, ).length + const abmelden = () => { + logout() + navigate('/login') + } + + const alleNavItems = + role === 'admin' + ? [...navItems, { to: '/benutzer', label: 'Benutzerverwaltung', icon: UserCog }] + : navItems + return (
{/* Sidebar */} -