diff --git a/backend/app/api/reports.py b/backend/app/api/reports.py index dc98739..5c15826 100644 --- a/backend/app/api/reports.py +++ b/backend/app/api/reports.py @@ -49,8 +49,9 @@ def dashboard( ) kpis = calculate_dashboard_kpis(db, jahr) + prev_kpis = calculate_dashboard_kpis(db, jahr - 1) sheet1 = calculate_sheet1_data(db, jahr) - return DashboardResponse(kpis=kpis, weekly=sheet1.get("weekly", [])) + return DashboardResponse(kpis=kpis, prev_kpis=prev_kpis, weekly=sheet1.get("weekly", [])) except ImportError: # report_service not yet implemented (parallel task) raise HTTPException(501, "Report service not yet available") diff --git a/backend/app/schemas/report.py b/backend/app/schemas/report.py index 8ee7157..0e42235 100644 --- a/backend/app/schemas/report.py +++ b/backend/app/schemas/report.py @@ -31,6 +31,7 @@ class DashboardResponse(BaseModel): """Combined dashboard payload: KPIs + weekly time-series.""" kpis: DashboardKPIs + prev_kpis: Optional[DashboardKPIs] = None weekly: list[WeeklyDataPoint] diff --git a/docs/todo.md b/docs/todo.md index 2362d28..0f7c3d0 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -4,14 +4,14 @@ - [x] **Gutachten-Statistik Seite** — ✅ Implementiert: 4 KPIs, Stacked-Bar (Typ pro KW), Donut (Typ-Verteilung), Grouped-Bar (Therapieänderungen pro KW), Horizontal-Bars (Gründe). Backend-Endpoint + Frontend komplett. - [ ] **Fallliste als Excel exportieren** — Export-Button auf der Cases-Seite für gefilterte Falllisten als .xlsx Download. Nutzt aktive Filter (Jahr, Fallgruppe, ICD-Status). -- [ ] **E-Mail-Benachrichtigungen bei Freigabe-Entscheidung** — DAK-Mitarbeiter per E-Mail informieren, wenn ihre Freigabe-Anfrage genehmigt oder abgelehnt wurde. SMTP ist bereits konfiguriert. +- [x] **E-Mail-Benachrichtigungen bei Freigabe-Entscheidung** — ✅ Implementiert: disclosure_service nutzt nun notification_service für In-App + E-Mail. Admins erhalten E-Mail bei neuer Anfrage, Mitarbeiter bei Genehmigung/Ablehnung. ## Mittlere Priorität -- [ ] **Benachrichtigungs-Center (Bell-Icon)** — UI-Element im Header mit Badge-Counter. `useNotifications()` Hook mit 60s-Polling existiert bereits, braucht nur ein Frontend-Element. +- [x] **Benachrichtigungs-Center (Bell-Icon)** — ✅ Bereits implementiert: Bell-Icon im Header mit Badge-Counter, Popover-Dropdown, Mark-as-read, 60s-Polling. War schon in Header.tsx vorhanden. - [ ] **Dashboard: Vorjahresvergleich bei KPIs** — Prozentuale Veränderung zum Vorjahr neben den KPI-Zahlen (z.B. "+12% vs. 2025"). `vorjahr_service` existiert im Backend. - [ ] **Batch-ICD-Eingabe** — Inline-Tabelle auf der ICD-Seite mit direkter ICD-Eingabe pro Zeile statt Einzelklick auf jeden Fall. -- [ ] **Dark Mode Toggle** — Toggle in Header oder Kontoverwaltung. `useTheme` Hook existiert bereits. +- [x] **Dark Mode Toggle** — ✅ Bereits implementiert: Sun/Moon-Toggle im Header, useTheme Hook aktiv. ## Niedrige Priorität diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index af516e0..92e82f3 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -4,7 +4,7 @@ import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, PieChart, Pie, Cell, ResponsiveContainer, } from 'recharts' -import { FileText, Clock, Code, Stethoscope, Info } from 'lucide-react' +import { FileText, Clock, Code, Stethoscope, Info, TrendingUp, TrendingDown, Minus } from 'lucide-react' import { useAuth } from '@/context/AuthContext' import { useDashboard, useYearlyComparison, useTopIcd } from '@/hooks/useDashboard' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' @@ -115,6 +115,8 @@ export function DashboardPage() { title="Fälle gesamt" tooltip="Gesamtzahl aller erfassten Fälle im gewählten Jahr" value={data.kpis.total_cases} + prevValue={data.prev_kpis?.total_cases} + prevYear={jahr - 1} icon={} href="/cases" /> @@ -122,6 +124,8 @@ export function DashboardPage() { title="Offene ICD" tooltip="Fälle, denen noch ein ICD-10-Diagnosecode zugewiesen werden muss" value={data.kpis.pending_icd} + prevValue={data.prev_kpis?.pending_icd} + prevYear={jahr - 1} icon={} href="/icd" /> @@ -129,6 +133,8 @@ export function DashboardPage() { title="Offene Codierung" tooltip="Fälle, die noch nicht abschließend klassifiziert wurden" value={data.kpis.pending_coding} + prevValue={data.prev_kpis?.pending_coding} + prevYear={jahr - 1} icon={} href={isAdmin ? '/coding' : undefined} /> @@ -136,6 +142,8 @@ export function DashboardPage() { title="Gutachten gesamt" tooltip="Anzahl der Fälle mit erstelltem Gutachten" value={data.kpis.total_gutachten} + prevValue={data.prev_kpis?.total_gutachten} + prevYear={jahr - 1} icon={} href="/gutachten-statistik" /> @@ -327,7 +335,20 @@ export function DashboardPage() { ) } -function KpiCard({ title, tooltip, value, icon, href }: { title: string; tooltip?: string; value: number; icon: React.ReactNode; href?: string }) { +function KpiCard({ title, tooltip, value, prevValue, prevYear, icon, href }: { + title: string + tooltip?: string + value: number + prevValue?: number + prevYear?: number + icon: React.ReactNode + href?: string +}) { + // Calculate percentage change vs previous year + const change = prevValue != null && prevValue > 0 + ? ((value - prevValue) / prevValue) * 100 + : null + const card = ( @@ -348,6 +369,35 @@ function KpiCard({ title, tooltip, value, icon, href }: { title: string; tooltip
{value.toLocaleString('de-DE')}
+ {prevValue != null && prevYear != null && ( +
+ {change !== null ? ( + change > 0 ? ( + <> + + +{Math.round(change)}% + + ) : change < 0 ? ( + <> + + {Math.round(change)}% + + ) : ( + <> + + 0% + + ) + ) : prevValue === 0 && value > 0 ? ( + Neu in {prevYear + 1} + ) : ( + + )} + {change !== null && ( + ggü. {prevYear} + )} +
+ )}
) diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 4aeca73..dd40817 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -112,6 +112,7 @@ export interface WeeklyDataPoint { export interface DashboardResponse { kpis: DashboardKPIs + prev_kpis?: DashboardKPIs | null weekly: WeeklyDataPoint[] }