v1.0.3: PDF footer fix, arrow alignment, email verification workflow, account deletion

This commit is contained in:
Pepe Ziberi
2026-02-21 16:45:44 +01:00
parent 25d3d553ff
commit b75bf9bb30
7 changed files with 278 additions and 6 deletions

View File

@@ -91,6 +91,10 @@ export function Topbar({
const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false)
const [isHoseSettingsOpen, setIsHoseSettingsOpen] = useState(false)
const [showPasswordDialog, setShowPasswordDialog] = useState(false)
const [showDeleteAccountDialog, setShowDeleteAccountDialog] = useState(false)
const [deleteAccountPw, setDeleteAccountPw] = useState('')
const [deleteAccountLoading, setDeleteAccountLoading] = useState(false)
const [deleteAccountError, setDeleteAccountError] = useState('')
const [pwOld, setPwOld] = useState('')
const [pwNew, setPwNew] = useState('')
const [pwConfirm, setPwConfirm] = useState('')
@@ -290,6 +294,13 @@ export function Topbar({
Administration
</DropdownMenuItem>
)}
<DropdownMenuItem
onClick={() => { setShowDeleteAccountDialog(true); setDeleteAccountPw(''); setDeleteAccountError('') }}
className="text-destructive focus:text-destructive"
>
<Trash2 className="w-4 h-4 mr-2" />
Konto löschen
</DropdownMenuItem>
<DropdownMenuItem onClick={onLogout} className="text-destructive focus:text-destructive">
<LogOut className="w-4 h-4 mr-2" />
Abmelden
@@ -539,6 +550,81 @@ export function Topbar({
</div>
</DialogContent>
</Dialog>
{/* Delete Account Dialog */}
<Dialog open={showDeleteAccountDialog} onOpenChange={setShowDeleteAccountDialog}>
<DialogContent className="max-w-sm">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 text-destructive">
<AlertTriangle className="w-5 h-5" />
Konto löschen
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<p className="text-sm text-muted-foreground">
Ihr Konto wird unwiderruflich gelöscht. Ihre Projekte und Daten bleiben der Organisation erhalten,
aber Ihr persönlicher Zugang wird entfernt.
</p>
{userRole === 'TENANT_ADMIN' && (
<div className="bg-amber-50 dark:bg-amber-950/30 rounded-lg p-3 text-xs text-amber-800 dark:text-amber-300 border border-amber-200 dark:border-amber-800">
<strong>Hinweis:</strong> Als einziger Administrator müssen Sie zuerst die Organisation unter Einstellungen löschen oder die Admin-Rolle übertragen.
</div>
)}
<div className="space-y-1.5">
<label className="text-sm font-medium">Passwort zur Bestätigung</label>
<input
type="password"
value={deleteAccountPw}
onChange={(e) => { setDeleteAccountPw(e.target.value); setDeleteAccountError('') }}
placeholder="Ihr Passwort"
className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
autoComplete="current-password"
/>
</div>
{deleteAccountError && (
<p className="text-sm text-destructive">{deleteAccountError}</p>
)}
<div className="flex gap-2 justify-end">
<Button
variant="outline"
size="sm"
onClick={() => setShowDeleteAccountDialog(false)}
disabled={deleteAccountLoading}
>
Abbrechen
</Button>
<Button
variant="destructive"
size="sm"
disabled={deleteAccountLoading || !deleteAccountPw}
onClick={async () => {
setDeleteAccountLoading(true)
setDeleteAccountError('')
try {
const res = await fetch('/api/auth/delete-account', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: deleteAccountPw }),
})
const data = await res.json()
if (res.ok) {
window.location.href = '/'
} else {
setDeleteAccountError(data.error || 'Löschung fehlgeschlagen')
}
} catch {
setDeleteAccountError('Verbindungsfehler')
} finally {
setDeleteAccountLoading(false)
}
}}
>
{deleteAccountLoading ? 'Wird gelöscht...' : 'Konto endgültig löschen'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</header>
)
}