From 29d32deef572472169fbdea16626424a9d91c20c Mon Sep 17 00:00:00 2001 From: ah-admin Date: Sun, 4 Oct 2026 15:07:01 +0200 Subject: [PATCH] v1.24: Anleitungsseite (Mieter/Verwaltung), Stand wird vor jedem Build automatisch aktualisiert --- .gitignore | 2 + deploy.sh | 12 +- kimi.md | 10 ++ package.json | 1 + scripts/anleitung-meta.mjs | 22 ++++ src/App.tsx | 2 + src/components/AppLayout.tsx | 2 + src/data/anleitung.ts | 224 +++++++++++++++++++++++++++++++++++ src/pages/Anleitung.tsx | 119 +++++++++++++++++++ 9 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 scripts/anleitung-meta.mjs create mode 100644 src/data/anleitung.ts create mode 100644 src/pages/Anleitung.tsx diff --git a/.gitignore b/.gitignore index e9c72b6..d19742d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ shots/ # Live-Stammdaten (PII) – nur lokal, nicht ins Git Master Data V.csv import-*.sql +# Wird vor jedem Build automatisch erzeugt (scripts/anleitung-meta.mjs) +src/data/anleitung-meta.ts .vite _ref/ *_recalc_verify.xlsx diff --git a/deploy.sh b/deploy.sh index f3ed373..7fd9c57 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Hinterstein Mieterportal – Deploy auf hocks.eu (Workflow aus dem Schulportal) # +# 0. Anleitungs-Stand aktualisieren (Hash + Datum der ausgelieferten Version) # 1. npm run build 2. Paket (dist + compose + nginx + server) hochladen # 3. Entpacken nach /opt/mieterportal, Container bauen & ersetzen # 4. Status prüfen (Container, Portal-Hash, API-Health) @@ -18,10 +19,13 @@ PSCP="/c/Program Files/PuTTY/pscp.exe" PLINK="/c/Program Files/PuTTY/plink.exe" REMOTE_DIR="/opt/mieterportal" -echo "==> 1/4 Frontend bauen" +echo "==> 1/5 Anleitungs-Stand aktualisieren" +node scripts/anleitung-meta.mjs + +echo "==> 2/5 Frontend bauen" npm run build -echo "==> 2/4 Paket erstellen und hochladen" +echo "==> 3/5 Paket erstellen und hochladen" rm -rf tmp-deploy && mkdir -p tmp-deploy/docker/templates cp -r dist tmp-deploy/dist cp docker-compose.yml tmp-deploy/ @@ -34,7 +38,7 @@ rm -rf tmp-deploy mp-dist.tar.gz "$SERVER":/tmp/mp-dist.tar.gz rm -f mp-dist.tar.gz -echo "==> 3/4 Auf Server entpacken, API-Image bauen, Stack ersetzen" +echo "==> 4/5 Auf Server entpacken, API-Image bauen, Stack ersetzen" "$PLINK" -batch -hostkey "$HOSTKEY" -i "$KEY" -P "$PORT" "$SERVER" " mkdir -p $REMOTE_DIR && tar -xzf /tmp/mp-dist.tar.gz -C $REMOTE_DIR && @@ -42,7 +46,7 @@ echo "==> 3/4 Auf Server entpacken, API-Image bauen, Stack ersetzen" cd $REMOTE_DIR && docker compose up -d --build " -echo "==> 4/4 Status prüfen" +echo "==> 5/5 Status prüfen" sleep 5 "$PLINK" -batch -hostkey "$HOSTKEY" -i "$KEY" -P "$PORT" "$SERVER" " docker ps --filter name=mp- --format 'table {{.Names}}\t{{.Status}}' && diff --git a/kimi.md b/kimi.md index 07ae0ca..2d85206 100644 --- a/kimi.md +++ b/kimi.md @@ -189,6 +189,16 @@ tar -czf mp-dist.tar.gz -C dist . ## 8. Verlauf / offene Punkte +**Erledigt 04.10. (v1.24):** Anleitungsseite /anleitung (Seite „Anleitung" im +Menü, sichtbar für Mieter und Verwaltung). Inhalte rollenbasiert in +src/data/anleitung.ts (Abschnitte je gruppe 'mieter'|'verwaltung'); Verwaltung +hat Reiter „Für die Verwaltung" / „Mieter-Ansicht". Stand-Badge (Datum + Build- +Hash) kommt aus src/data/anleitung-meta.ts, die von scripts/anleitung-meta.mjs +erzeugt wird – Aufruf vor jedem Build: als package.json „prebuild"-Hook (läuft +also bei JEDEM npm run build mit) plus eigener Schritt 1 in deploy.sh (1/5…5/5). +Meta-Datei ist gitignored (sonst Hash-Henne-Ei: Committen ändert den Hash). +Dunkelmodus-taugliche Klassen beachten. + **Erledigt 04.10. (v1.23):** Nebenkostenabrechnung als PDF direkt aus dem Kosten- und Zählerstandsmodul. Neues Modul `server/src/abrechnung.js` (pdfkit, neue Dependency – Achtung: Standardfont Helvetica/WinAnsi, kein diff --git a/package.json b/package.json index 5a1d28b..ff11f27 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "vite", + "prebuild": "node scripts/anleitung-meta.mjs", "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview" diff --git a/scripts/anleitung-meta.mjs b/scripts/anleitung-meta.mjs new file mode 100644 index 0000000..67f074c --- /dev/null +++ b/scripts/anleitung-meta.mjs @@ -0,0 +1,22 @@ +// Aktualisiert den Build-Stand der Anleitungsseite. +// Wird bei jedem Deploy (deploy.sh, Schritt 1) VOR dem Frontend-Build aufgerufen, +// damit Anleitung und ausgelieferte Version garantiert zusammenpassen. +import { execSync } from 'node:child_process' +import { writeFileSync } from 'node:fs' + +const hash = execSync('git rev-parse --short HEAD').toString().trim() +const datum = new Date().toLocaleString('de-DE', { + day: '2-digit', month: '2-digit', year: 'numeric', + hour: '2-digit', minute: '2-digit', +}) + +const inhalt = `// Von scripts/anleitung-meta.mjs erzeugt – nicht von Hand ändern. +// Wird bei jedem Deploy vor dem Build aktualisiert. +export const anleitungMeta = { + hash: '${hash}', + datum: '${datum}', +} +` + +writeFileSync('src/data/anleitung-meta.ts', inhalt) +console.log(`Anleitungs-Stand aktualisiert: Build ${hash} · ${datum}`) diff --git a/src/App.tsx b/src/App.tsx index e6cf31f..28617ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,7 @@ import Kosten from './pages/Kosten' import Bestand from './pages/Bestand' import BenutzerVerwaltung from './pages/BenutzerVerwaltung' import Profil from './pages/Profil' +import Anleitung from './pages/Anleitung' /** Fängt Render-Fehler ab – statt schwarzem Bildschirm: Hinweis + Reload. */ class FehlerGrenze extends Component<{ children: ReactNode }, { fehler: boolean }> { @@ -89,6 +90,7 @@ export default function App() { } /> } /> } /> + } /> } /> } /> diff --git a/src/components/AppLayout.tsx b/src/components/AppLayout.tsx index 75d7fd4..0d1ff7d 100644 --- a/src/components/AppLayout.tsx +++ b/src/components/AppLayout.tsx @@ -11,6 +11,7 @@ import { Gauge, Calculator, UserRound, + CircleHelp, } from 'lucide-react' import { useStore } from '../store/store' import ThemeToggle from './ThemeToggle' @@ -24,6 +25,7 @@ const navItems = [ { to: '/kosten', label: 'Steuer & Kosten', icon: Calculator }, { to: '/dokumente', label: 'Dokumente', icon: FileText }, { to: '/nachrichten', label: 'Nachrichten', icon: MessagesSquare }, + { to: '/anleitung', label: 'Anleitung', icon: CircleHelp }, { to: '/profil', label: 'Mein Profil', icon: UserRound }, ] diff --git a/src/data/anleitung.ts b/src/data/anleitung.ts new file mode 100644 index 0000000..628abe5 --- /dev/null +++ b/src/data/anleitung.ts @@ -0,0 +1,224 @@ +// Inhalt der Anleitungsseite (/anleitung). +// Pro Abschnitt: Zielgruppe, Titel, kurze Punkte. Wird auf der Seite +// rollenbasiert gefiltert (Mieter sehen nur Mieter-Inhalte). + +export type AnleitungGruppe = 'mieter' | 'verwaltung' + +export interface AnleitungPunkt { + titel: string + text: string +} + +export interface AnleitungAbschnitt { + gruppe: AnleitungGruppe + titel: string + punkte: AnleitungPunkt[] +} + +export const anleitungIntro: Record = { + mieter: + 'Willkommen im Mieterportal der Hinterstein Immobilien. Hier finden Sie eine Kurzanleitung ' + + 'zu allen Funktionen, die Ihnen als Mieter zur Verfügung stehen.', + verwaltung: + 'Arbeitshilfe für die Verwaltung: Aufbau und Bedienung aller Verwaltungsfunktionen im Portal. ' + + 'Der Reiter „Mieter-Ansicht“ zeigt, was Mieter sehen.', +} + +export const anleitungAbschnitte: AnleitungAbschnitt[] = [ + { + gruppe: 'mieter', + titel: 'Anmeldung & Zugang', + punkte: [ + { + titel: 'Einloggen', + text: 'Auf der Startseite Benutzername und Passwort eingeben. Die Anmeldung bleibt im Browser erhalten; über „Abmelden“ in der Seitenleiste wird die Sitzung beendet.', + }, + { + titel: 'Passwort', + text: 'Das Passwort erhalten Sie von der Verwaltung. Ändern Sie es nach dem ersten Login in „Mein Profil“.', + }, + { + titel: 'Passwort vergessen', + text: 'Bitte die Verwaltung kontaktieren (joseph@hinterstein-immo.de) – sie setzt ein neues Passwort.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Dashboard', + punkte: [ + { + titel: 'Alles auf einen Blick', + text: 'Ankündigungen der Verwaltung, offene Beträge und ungelesene Nachrichten finden Sie auf der Startseite nach dem Login.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Mietkonto & Zahlungen', + punkte: [ + { + titel: 'Saldo prüfen', + text: 'Aktueller Kontostand, offene und erledigte Buchungen Ihrer Wohnung sind unter „Mietkonto & Zahlungen“ einsehbar.', + }, + { + titel: 'Überweisen', + text: 'Überweisen Sie offene Beträge an die Bankverbindung der Verwaltung. Eingehende Zahlungen werden von der Verwaltung gebucht.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Schäden melden', + punkte: [ + { + titel: 'Schaden erfassen', + text: 'Unter „Schadensmeldungen“ einen neuen Schaden mit Wohnung, Kategorie, Beschreibung und Foto melden.', + }, + { + titel: 'Automatische Benachrichtigung', + text: 'Zu jeder Meldung wird automatisch eine Nachricht eröffnet und die Verwaltung per E-Mail informiert – Sie erhalten eine Bestätigungsmail.', + }, + { + titel: 'Verlauf verfolgen', + text: 'Antworten und Status der Verarbeitung finden Sie im Nachrichten-Modul. Neue Antworten kommen zusätzlich per E-Mail.', + }, + { + titel: 'Erstantwort', + text: 'Die erste Rückmeldung kann automatisch von einer KI kommen und wird anschließend von der Verwaltung geprüft.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Zählerstände', + punkte: [ + { + titel: 'Stände eintragen', + text: 'Unter „Zählerstände“ sehen Sie die Strom-, Wasser- und Gaszähler Ihrer Wohnung und tragen neue Stände mit Datum ein.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Dokumente', + punkte: [ + { + titel: 'Unterlagen abrufen', + text: 'Mietvertrag, Nebenkostenabrechnungen und weitere Dokumente Ihrer Wohnung können Sie hier ansehen und herunterladen.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Nachrichten', + punkte: [ + { + titel: 'Mit der Verwaltung schreiben', + text: 'Alle Unterhaltungen mit der Verwaltung laufen gebündelt hier. Ungelesene Antworten sind im Menü mit einer Zahl gekennzeichnet.', + }, + ], + }, + { + gruppe: 'mieter', + titel: 'Mein Profil', + punkte: [ + { + titel: 'Stammdaten pflegen', + text: 'Halten Sie Telefonnummer, E-Mail-Adresse und Bankverbindung (Kontoinhaber, IBAN, Bankname) aktuell – die Verwaltung nutzt diese Daten für Abrechnung und Kontakt.', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Benutzerverwaltung', + punkte: [ + { + titel: 'Mieter anlegen & pflegen', + text: 'Neue Mieter mit Benutzername, Name und E-Mail-Adresse anlegen, bearbeiten oder deaktivieren. Jedem Mieter ist seine Wohneinheit zugeordnet.', + }, + { + titel: 'Passwort zurücksetzen', + text: 'Bei „Passwort vergessen“ vergeben Sie ein neues Erst-Passwort (z. B. 0815) – der Mieter sollte es danach in „Mein Profil“ ändern.', + }, + { + titel: 'Registrierungen prüfen', + text: 'Neue Registrierungen von Mietern werden automatisch per E-Mail an die Verwaltung gemeldet und hier geprüft.', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Häuser & Wohnungen', + punkte: [ + { + titel: 'Haus anlegen', + text: 'Pflichtangaben: Baujahr, Adresse, Etagenhöhe, Stockwerke, Anzahl Wohneinheiten und Quadratmeter.', + }, + { + titel: 'Wohnungen zuordnen', + text: 'Jede Wohnung gehört zu einem Haus: Etage, Wohnfläche, Kalt-/Warmmiete, Mieter und Status (vermietet/frei) je Einheit pflegen. Der Bestand erscheint anschließend im Dashboard.', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Steuer & Kosten', + punkte: [ + { + titel: 'Kosten erfassen', + text: 'Kostenpositionen je Haus oder Wohnung anlegen und als umlegbar/nicht umlegbar markieren – Grundlage der Nebenkostenabrechnung.', + }, + { + titel: 'Belege aus Spark Receipt', + text: 'Gescannte Belege werden per OCR analysiert und primär über die Stromzählernummer, sekundär über die Adresse der Wohnung zugeordnet.', + }, + { + titel: 'Abrechnung erzeugen', + text: 'Im Tab „Kosten“ über „Abrechnung erzeugen“ Wohnung und Jahr wählen: Das Portal erstellt eine druckfertige Nebenkostenabrechnung als PDF und legt sie im Dokumentenmodul ab.', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Schadensmeldungen & Kommunikation', + punkte: [ + { + titel: 'Meldungen bearbeiten', + text: 'Jede Schadensmeldung startet automatisch eine Nachricht. Ihre Antworten werden per E-Mail an den Mieter versendet.', + }, + { + titel: 'KI-Unterstützung (n8n)', + text: 'Das Automatisierungssystem erzeugt zu Schadensmeldungen automatisch eine Antwort, die ins Portal übernommen und von Ihnen geprüft bzw. ergänzt wird.', + }, + { + titel: 'Direkt-E-Mail an Mieter', + text: 'Im Nachrichten-Modul können Sie E-Mails direkt an Mieter senden; die Verwaltung erhält automatisch eine Kopie (CC).', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Dokumente & Zählerstände', + punkte: [ + { + titel: 'Dokumente hochladen', + text: 'PDFs (z. B. Mietverträge, Abrechnungen) hochladen und Kategorie sowie Wohnung zuordnen. Mieter sehen nur Dokumente ihrer eigenen Wohnung.', + }, + { + titel: 'Zähler pflegen', + text: 'Zähler je Wohnung mit Zählernummer und Zählerart anlegen und die von Mietern erfassten Stände prüfen.', + }, + ], + }, + { + gruppe: 'verwaltung', + titel: 'Darstellung', + punkte: [ + { + titel: 'Hell / Dunkel', + text: 'Das Design lässt sich über den Schalter in der Seitenleiste (Desktop) bzw. oben rechts (Mobil) zwischen hellem und dunklem Modus wechseln.', + }, + ], + }, +] diff --git a/src/pages/Anleitung.tsx b/src/pages/Anleitung.tsx new file mode 100644 index 0000000..ff79cec --- /dev/null +++ b/src/pages/Anleitung.tsx @@ -0,0 +1,119 @@ +import { useMemo, useState } from 'react' +import { + Building2, + Calculator, + CircleHelp, + FileText, + Gauge, + Info, + KeyRound, + LayoutDashboard, + MessagesSquare, + Paintbrush, + UserRound, + Users, + Wallet, + Wrench, +} from 'lucide-react' +import { useStore } from '../store/store' +import { + anleitungAbschnitte, + anleitungIntro, + type AnleitungAbschnitt, + type AnleitungGruppe, +} from '../data/anleitung' +import { anleitungMeta } from '../data/anleitung-meta' + +const Icons: Record = { + 'Anmeldung & Zugang': KeyRound, + Dashboard: LayoutDashboard, + 'Mietkonto & Zahlungen': Wallet, + 'Schäden melden': Wrench, + Zählerstände: Gauge, + Dokumente: FileText, + Nachrichten: MessagesSquare, + 'Mein Profil': UserRound, + Benutzerverwaltung: Users, + 'Häuser & Wohnungen': Building2, + 'Steuer & Kosten': Calculator, + 'Schadensmeldungen & Kommunikation': MessagesSquare, + 'Dokumente & Zählerstände': FileText, + Darstellung: Paintbrush, +} + +function AbschnittKarte({ abschnitt }: { abschnitt: AnleitungAbschnitt }) { + const Icon = Icons[abschnitt.titel] ?? Info + return ( +
+

+ + + + {abschnitt.titel} +

+
    + {abschnitt.punkte.map((p) => ( +
  • +
    {p.titel}
    +

    {p.text}

    +
  • + ))} +
+
+ ) +} + +export default function Anleitung() { + const { role } = useStore() + const istVerwaltung = role === 'admin' + const [tab, setTab] = useState('verwaltung') + + // Mieter sehen nur Mieter-Inhalte; die Verwaltung wählt zwischen beiden Ansichten. + const gruppe: AnleitungGruppe = istVerwaltung ? tab : 'mieter' + + const abschnitte = useMemo( + () => anleitungAbschnitte.filter((a) => a.gruppe === gruppe), + [gruppe], + ) + + return ( +
+
+
+

+ + Anleitung +

+

{anleitungIntro[gruppe]}

+
+
+ Stand {anleitungMeta.datum} · Build {anleitungMeta.hash} +
+
+ + {istVerwaltung && ( +
+ {([['verwaltung', 'Für die Verwaltung'], ['mieter', 'Mieter-Ansicht']] as const).map(([wert, label]) => ( + + ))} +
+ )} + +
+ {abschnitte.map((a) => ( + + ))} +
+
+ ) +}