v1.8: Schadensmeldung startet Unterhaltung + Bestätigungsmail; 2. Verwaltungspostfach axel@hocks.eu

This commit is contained in:
2026-10-02 14:16:30 +02:00
parent a02267c732
commit e83a61fc0a
3 changed files with 69 additions and 3 deletions
+11
View File
@@ -189,6 +189,17 @@ tar -czf mp-dist.tar.gz -C dist .
## 8. Verlauf / offene Punkte
**Erledigt 02.10. (v1.8):** Schadensmeldungen starten jetzt automatisch eine
Unterhaltung (Thread „Schadensmeldung: …" mit erster Nachricht inkl. Kategorie/
Priorität, ungelesen für die Gegenseite) – Antworten darauf lösen über
`/api/threads/:id/messages` wie gehabt Mails an Verwaltung + Mieter aus.
Zusätzlich Eingangsbestätigungs-Mail an den Mieter bei jeder Meldung.
Verwaltungs-Mails gehen jetzt an zwei echte Postfächer: joseph@hinterstein-immo.com
(Benutzer j.hinterstein, Seed + Live-DB geändert) und axel@hocks.eu (neuer
Verwaltungsbenutzer a.hocks, Passwort 0815, Seed + Live-DB). E2E verifiziert:
Meldung + Antwort → je Mail an beide Postfächer zugestellt (Postfix status=sent);
Mieter-Adressen der Demoseeds sind example.de und bouncen bewusst.
**Erledigt 02.10. (v1.7):** Responsive Layout repariert. Sidebar skaliert jetzt in zwei
Stufen (`w-64` ab `lg`, `xl:w-72`) statt fixer 288 px. Mobile Ansicht (Top-Bar +
horizontal scrollende Nav) gilt jetzt bis 1024 px (`md:hidden` → `lg:hidden`) – vorher
+44
View File
@@ -257,6 +257,27 @@ app.post('/api/tickets', auth, async (req, res, next) => {
String(category || 'Sonstiges'), ['niedrig', 'mittel', 'hoch'].includes(priority) ? priority : 'mittel',
deutschesDatum()],
)
// ── Automatische Unterhaltung (Kommunikation) zur Meldung anlegen ──
// Jede Schadensmeldung startet einen eigenen Thread; Antworten darauf
// lösen über /api/threads/:id/messages bereits Mails an beide Seiten aus.
const threadId = `m-${Date.now()}`
const vonRolle = req.user.rolle === 'verwaltung' ? 'admin' : 'tenant'
const threadBetreff = `Schadensmeldung: ${String(title).trim()}`
const ersteNachricht =
`Schadensmeldung eingegangen – Kategorie: ${String(category || 'Sonstiges')}, ` +
`Priorität: ${['niedrig', 'mittel', 'hoch'].includes(priority) ? priority : 'mittel'}.\n\n` +
`${String(title).trim()}\n${String(description || '–').trim()}`
await pool.query(
`INSERT INTO threads (id, unit_id, tenant, subject, unread_by_admin, unread_by_tenant)
VALUES ($1,$2,$3,$4,$5,$6)`,
[threadId, unitId, mieterName, threadBetreff,
vonRolle === 'tenant', vonRolle === 'admin'],
)
await pool.query(
`INSERT INTO messages (id, thread_id, from_role, author, text, at) VALUES ($1,$2,$3,$4,$5,$6)`,
[`${threadId}-1`, threadId, vonRolle, mieterName, ersteNachricht, deutschesDatumZeit()],
)
const label = mail.einheitLabel(mapUnit(unitRows[0]))
const betreff = `Neue Schadensmeldung: ${String(title).trim()} (${label})`
const text =
@@ -277,6 +298,29 @@ app.post('/api/tickets', auth, async (req, res, next) => {
aktionenHtml: mail.buttonHtml('Zum Mieterportal', `${process.env.PORTAL_URL || 'https://mieterportal.hocks.eu'}/schadensmeldungen`),
})
mail.benachrichtigeVerwaltung(betreff, text, html)
// Eingangsbestätigung an den Mieter (bei internen Meldungen der
// Verwaltung entfällt sie, da diese keine Mieter-E-Mail auslösen soll)
if (vonRolle === 'tenant') {
const bestText =
`Guten Tag,\n\nvielen Dank für Ihre Schadensmeldung „${String(title).trim()}“. ` +
`Diese ist bei uns eingegangen und wird von der Verwaltung bearbeitet.\n\n` +
`Wohnung: ${label}\nKategorie: ${String(category || 'Sonstiges')}\n\n` +
`Sie können den Fortschritt im Mieterportal verfolgen und sich jederzeit ` +
`in der zugehörigen Unterhaltung („${threadBetreff}“) an uns wenden.`
const bestHtml = mail.baueHtmlMail({
titel: 'Schadensmeldung eingegangen',
untertitel: label,
einleitung: `Vielen Dank für Ihre Schadensmeldung „${String(title).trim()}“. Diese ist bei uns eingegangen und wird von der Verwaltung bearbeitet.`,
zeilen: [
['Wohnung', label],
['Kategorie', String(category || 'Sonstiges')],
],
statusBox: { hintergrund: '#f0fdfa', farbe: '#0f766e', text: 'Status: offen – eingegangen' },
zitat: `${String(title).trim()}\n${String(description || '–').trim()}`,
aktionenHtml: mail.buttonHtml('Meldung im Portal ansehen', `${process.env.PORTAL_URL || 'https://mieterportal.hocks.eu'}/nachrichten`),
})
mail.benachrichtigeMieter(unitId, `Eingangsbestätigung: ${String(title).trim()}`, bestText, bestHtml)
}
res.json({ ticket: mapTicket(rows[0]) })
} catch (e) { next(e) }
})
+14 -3
View File
@@ -1,9 +1,9 @@
/** Handgeschriebene Demo-Daten (vormals src/data/seed.ts).
*
* units + transactions kommen maschinell aus seed/generated.json
* (beispieldaten.py). Die Verwaltungs-E-Mail ist bewusst ein echtes,
* zustellbares Postfach (axel@hocks.eu), damit Benachrichtigungen ankommen;
* im Portal änderbar (Benutzerverwaltung).
* (beispieldaten.py). Verwaltungs-E-Mails sind bewusst echte, zustellbare
* Postfächer (joseph@hinterstein-immo.com, axel@hocks.eu), damit
* Benachrichtigungen ankommen; im Portal änderbar (Benutzerverwaltung).
*/
const DEMO_HASH = 'c57a5df4f97ed6914116e5fd1348406064834f22503d890eb571af442ac4b4c3' // sha256("0815")
@@ -15,6 +15,17 @@ const users = [
name: 'Joseph Hinterstein',
passwort_hash: DEMO_HASH,
rolle: 'verwaltung',
email: 'joseph@hinterstein-immo.com',
einheit_id: null,
aktiv: true,
erstellt_am: '01.03.2024',
},
{
id: 'b-axel',
benutzername: 'a.hocks',
name: 'Axel Hocks',
passwort_hash: DEMO_HASH,
rolle: 'verwaltung',
email: 'axel@hocks.eu',
einheit_id: null,
aktiv: true,