feat: Add admin access denial alert to Dashboard with timeout handling #62

Merged
Lars merged 1 commits from develop into main 2026-04-05 11:29:04 +02:00
2 changed files with 34 additions and 8 deletions

View File

@ -17,7 +17,7 @@
| # | Titel (gekürzt) | Vorschlag | Notiz |
|---|-----------------|-----------|--------|
| 14 | Icon Picker Trainingstypen | OFFEN | Nur Freitext-Feld `icon` |
| 14 | Icon Picker Trainingstypen | **ERLEDIGT** | `EmojiIconPicker` in `AdminTrainingTypesPage.jsx`; Gitea #14 geschlossen (2026-04) |
| 15 | Quality-Filter KI & Charts | TEILWEISE | Registry/Charts Confidence, kein durchgängiges Produkt |
| 21 | Universeller CSV-Parser + Mapping | OFFEN | Modul-spezifische Parser |
| 25 | Ziele-System Goals | DONE? | GoalsPage, Router, Focus Areas |
@ -48,14 +48,11 @@
## Details je Issue (älteste zuerst)
### #14 [FEAT-001] Icon Picker für Trainingstypen
### #14 [FEAT-001] Icon Picker für Trainingstypen — **ERLEDIGT**
**Code-Stand:** `AdminTrainingTypesPage.jsx` nutzt ein **Textfeld** `icon` (frei, z. B. Emoji). Kein dedizierter Icon-Picker (Palette, Vorschau, Kategorien).
**Code-Stand:** `AdminTrainingTypesPage.jsx` nutzt **`EmojiIconPicker`** (`frontend/src/components/EmojiIconPicker.jsx`) für Neu- und Bearbeiten-Dialoge; Wert bleibt String in `training_types.icon`.
**Vorschlag:** `OFFEN` Issue beibehalten; optional präzisieren: „Emoji-Picker oder vordefinierte Icon-Liste statt Freitext“.
**Kommentar-Entwurf für Gitea:**
> Stand Backend/Frontend: `icon` wird als String in `training_types` gespeichert, Eingabe ist Freitext. Icon-Picker-UX steht noch aus.
**Gitea:** Issue **#14 geschlossen** (Kommentar + Close, Stand 2026-04).
---

View File

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useLocation } from 'react-router-dom'
import { Check, Brain } from 'lucide-react'
import {
LineChart, Line, XAxis, YAxis, Tooltip,
@ -252,8 +252,11 @@ function ComboChart({ weights, nutrition }) {
// Main Dashboard
export default function Dashboard() {
const nav = useNavigate()
const location = useLocation()
const { activeProfile } = useProfile()
const [adminDeniedHint, setAdminDeniedHint] = useState(false)
const [stats, setStats] = useState(null)
const [weights, setWeights] = useState([])
const [calipers, setCalipers] = useState([])
@ -299,6 +302,14 @@ export default function Dashboard() {
useEffect(()=>{ load() },[])
useEffect(() => {
if (!location.state?.adminDenied) return
setAdminDeniedHint(true)
nav('.', { replace: true, state: null })
const clear = window.setTimeout(() => setAdminDeniedHint(false), 8000)
return () => window.clearTimeout(clear)
}, [location.state, nav])
if (loading) return <div className="empty-state"><div className="spinner"/></div>
const latestCal = calipers[0]
@ -349,6 +360,24 @@ export default function Dashboard() {
return (
<div className="dashboard-page">
{adminDeniedHint && (
<div
role="alert"
style={{
marginBottom: 14,
padding: '12px 14px',
borderRadius: 10,
background: '#FCEBEB',
border: '1px solid #D85A3033',
fontSize: 13,
color: '#D85A30',
lineHeight: 1.5,
}}
>
<strong>Kein Admin-Zugriff.</strong> Dieser Bereich ist nur für Konten mit Administrator-Rolle.
Du wurdest zur Übersicht weitergeleitet.
</div>
)}
{/* Header greeting */}
<div className="dashboard-greeting">
<h1 style={{fontSize:22,fontWeight:800,margin:0,color:'var(--text1)'}}>