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

@@ -0,0 +1,160 @@
<script setup lang="ts">
import type { useBugReport } from '~/composables/useBugReport'
/**
* The composable stays owned by the page: the HUD's "Report issue" button needs
* `openBugReport` and `bugReportCapturing` before this dialog ever renders. So it
* is handed over whole rather than unpacked into a dozen props.
*/
const props = defineProps<{
bug: ReturnType<typeof useBugReport>
theme: 'light' | 'dark'
}>()
const {
showBugReportDialog,
bugReportComment,
bugReportContact,
bugReportScreenshot,
bugReportArrows,
bugReportLoading,
bugReportError,
bugReportSuccess,
bugReportCode,
bugReportCanvasRef,
bugReportImgRef,
setupAnnotationCanvas,
onCanvasMouseDown,
onCanvasMouseMove,
onCanvasMouseUp,
onCanvasMouseLeave,
undoLastArrow,
submitBugReport,
} = props.bug
</script>
<template>
<v-dialog v-model="showBugReportDialog" max-width="680" scrollable :content-class="theme === 'light' ? 'pm-dialog-light' : ''">
<v-card :class="theme === 'light' ? 'bg-white border border-black/10 text-[#0f1420]' : 'bg-[#0b101d] border border-white/10 text-white'">
<v-card-title class="flex items-center gap-2 text-base font-semibold pt-4 px-5">
<v-icon icon="mdi-bug-outline" color="#f87171" size="20" />
Fehler melden
</v-card-title>
<v-card-text class="space-y-4 px-5 pb-2">
<div v-if="bugReportSuccess" class="rounded-xl bg-emerald-500/10 border border-emerald-400/30 p-4 text-center">
<v-icon icon="mdi-check-circle-outline" color="emerald" size="32" class="mb-2" />
<p class="text-emerald-300 font-semibold">Danke! Bug Report wurde gesendet.</p>
<p v-if="bugReportCode" class="mt-2 text-xs text-emerald-200/70">
Fehlercode <span class="font-mono">{{ bugReportCode }}</span>
</p>
</div>
<template v-else>
<div v-if="bugReportScreenshot" class="space-y-2">
<div class="flex items-start justify-between gap-3">
<div class="flex items-start gap-2">
<v-icon size="18" color="red" class="mt-0.5">mdi-gesture-tap-button</v-icon>
<div>
<p class="text-sm font-semibold text-white/90">Wo ist der Fehler? Zeichne einen Pfeil hin.</p>
<p class="text-xs text-white/55 leading-snug">
Klicke auf die Stelle und ziehe mit gedrückter Maustaste (am Handy: mit dem Finger)
zur Problemstelle. Du kannst mehrere Pfeile setzen.
</p>
</div>
</div>
<v-btn
v-if="bugReportArrows.length > 0"
size="x-small"
variant="text"
color="red"
prepend-icon="mdi-undo"
class="shrink-0"
@click="undoLastArrow"
>
Pfeil zurück
</v-btn>
</div>
<div class="relative rounded-xl overflow-hidden border border-white/10" style="line-height:0">
<img
ref="bugReportImgRef"
:src="bugReportScreenshot"
class="w-full block"
alt="Screenshot"
@load="setupAnnotationCanvas"
/>
<canvas
ref="bugReportCanvasRef"
class="absolute inset-0 w-full h-full cursor-crosshair select-none"
style="touch-action:none"
@pointerdown="onCanvasMouseDown"
@pointermove="onCanvasMouseMove"
@pointerup="onCanvasMouseUp"
@pointerleave="onCanvasMouseLeave"
@pointercancel="onCanvasMouseLeave"
/>
<span
v-if="bugReportArrows.length === 0"
class="pointer-events-none absolute inset-0 flex items-center justify-center text-center px-4"
>
<span class="rounded-full bg-black/55 px-3 py-1 text-xs text-white/85 backdrop-blur">
Hier ziehen, um einen Pfeil zur Fehlerstelle zu zeichnen
</span>
</span>
</div>
</div>
<div v-else class="rounded-xl border border-white/10 bg-white/5 p-4 text-center text-sm text-white/50">
Kein Screenshot verfügbar
</div>
<v-textarea
v-model="bugReportComment"
label="Fehlerbeschreibung / Featurewunsch"
placeholder="Was ist kaputt? Was sollte stattdessen passieren?"
persistent-placeholder
variant="outlined"
color="red"
rows="3"
auto-grow
maxlength="2000"
counter="2000"
hide-details="auto"
:disabled="bugReportLoading"
/>
<v-text-field
v-model="bugReportContact"
label="Dein Name / Kontakt"
variant="outlined"
color="red"
density="comfortable"
hide-details
:disabled="bugReportLoading"
/>
<v-alert
v-if="bugReportError"
type="error"
density="compact"
variant="tonal"
class="bg-red-500/10 text-red-200"
>
{{ bugReportError }}
</v-alert>
</template>
</v-card-text>
<v-card-actions v-if="!bugReportSuccess" class="justify-end gap-2 px-5 pb-4">
<v-btn variant="text" color="grey" :disabled="bugReportLoading" @click="showBugReportDialog = false">
Abbrechen
</v-btn>
<v-btn
color="red"
variant="flat"
:loading="bugReportLoading"
:disabled="!bugReportComment.trim()"
@click="submitBugReport"
>
Bug melden
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>