feat(bug-reports): trace reports by code and tell Live ATC from Classroom

Every report now gets a UUID that the notification mail quotes as
"Nenne den Fehlercode <uuid> beim Commit", so a fix can be tied back to
the report it closes. The mail also names the area it came from, and the
comment field is relabelled "Fehlerbeschreibung/Featurewunsch" since it
collects feature wishes just as often as bugs.

Classroom loses its plain feedback link and gets the same reporter as
Live ATC — screenshot, arrow annotation and all. `useBugReport` takes an
options object so the communications-engine snapshot stays optional; the
dialog moves out of `live-atc/cockpit` now that both pages use it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
itsrubberduck
2026-07-26 21:11:43 +02:00
parent 4dbd0bda9f
commit 034ddbab2e
6 changed files with 116 additions and 34 deletions

View File

@@ -1,22 +1,35 @@
import { defineEventHandler, readBody, createError } from 'h3'
import { randomUUID } from 'node:crypto'
import { requireUserSession } from '../../utils/auth'
import { BugReport } from '../../models/BugReport'
import { BugReport, type BugReportSource } from '../../models/BugReport'
import { sendMail } from '../../utils/notifications'
const SOURCE_LABELS: Record<BugReportSource, string> = {
'live-atc': 'Live ATC',
classroom: 'Classroom',
}
export default defineEventHandler(async (event) => {
const user = await requireUserSession(event)
const body = await readBody(event)
const comment = String(body?.comment ?? '').trim()
if (!comment) {
throw createError({ statusCode: 400, statusMessage: 'Kommentar ist erforderlich' })
throw createError({ statusCode: 400, statusMessage: 'Fehlerbeschreibung ist erforderlich' })
}
const contact = String(
body?.contact || [user.name, user.email].filter(Boolean).join(' — ')
).slice(0, 200)
const source: BugReportSource = body?.source === 'classroom' ? 'classroom' : 'live-atc'
// The code is what ties a report to the commit that fixes it — it goes into
// the mail so it can be pasted straight into the commit message.
const code = randomUUID()
const report = await BugReport.create({
code,
source,
comment: comment.slice(0, 4000),
contact,
userId: user._id,
@@ -25,20 +38,32 @@ export default defineEventHandler(async (event) => {
})
const adminUrl = `${process.env.APP_URL || 'https://app.opensquawk.de'}/admin`
const sourceLabel = SOURCE_LABELS[source]
const stateInfo = body?.pmState?.currentStateId
? `State: ${body.pmState.currentStateId} (Flow: ${body.pmState.flowSlug || '—'})`
: ''
await sendMail({
to: 'emanuel@faktorxmensch.com',
subject: `[OpenSquawk Bug] ${contact}`,
subject: `[OpenSquawk Bug · ${sourceLabel}] ${contact}`,
html: `<h2>Neuer Bug Report</h2>
<p><strong>Bereich:</strong> ${sourceLabel}</p>
<p><strong>Von:</strong> ${contact}</p>
<p><strong>Kommentar:</strong><br>${comment.replace(/\n/g, '<br>')}</p>
<p><strong>Fehlerbeschreibung/Featurewunsch:</strong><br>${comment.replace(/\n/g, '<br>')}</p>
<p><strong>Nenne den Fehlercode ${code} beim Commit.</strong></p>
${stateInfo ? `<p><strong>${stateInfo}</strong></p>` : ''}
<p><a href="${adminUrl}">Im Admin-Panel ansehen →</a></p>`,
text: `Bug Report von ${contact}\n\n${comment}\n${stateInfo}\n\nAdmin: ${adminUrl}`,
text: `Bug Report von ${contact}
Bereich: ${sourceLabel}
Fehlerbeschreibung/Featurewunsch:
${comment}
Nenne den Fehlercode ${code} beim Commit.
${stateInfo}
Admin: ${adminUrl}`,
}).catch(() => {})
return { success: true, id: String(report._id) }
return { success: true, id: String(report._id), code }
})