diff --git a/docs/todo.md b/docs/todo.md index 3284dc52..7b8eebe1 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,13 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🎂 Anstehende Geburtstage auch im Dashboard** + - Die Geburtstags-Sektion gab es bisher nur im Vertrags-Cockpit. + Jetzt 1:1 auch auf dem Dashboard (nur Mitarbeiter/Admin, + `enabled: !isCustomer`), gleiche `birthdayApi.getUpcoming(7, 30)`- + Query + identische Karten-Darstellung (Heute/vergangen/kommend, + Link auf die Kundenakte). Kein Backend-Change nötig. + - [x] **🔴 Pentest R121 – Audit-Verify: Fehlalarm „manipuliert" bei leerem resourceId** - `POST /api/audit-logs/verify` meldete ~73% der Einträge als manipuliert. Kein echtes Tampering, sondern ein Bug in diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index 8f89df43..1cc43f4b 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -2,11 +2,14 @@ import { useState, useMemo } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; -import { customerApi, contractApi, contractTaskApi, appSettingsApi } from '../services/api'; +import { customerApi, contractApi, contractTaskApi, appSettingsApi, birthdayApi } from '../services/api'; import { buildContractLabelParts } from '../utils/contractLabel'; +import { pushHistory } from '../utils/navigation'; +import { formatDate } from '../utils/dateFormat'; import Card from '../components/ui/Card'; import Button from '../components/ui/Button'; import Input from '../components/ui/Input'; +import Badge from '../components/ui/Badge'; import Modal from '../components/ui/Modal'; import { Users, @@ -20,6 +23,7 @@ import { Plus, Clock, XCircle, + Cake, } from 'lucide-react'; import type { Contract } from '../types'; @@ -79,6 +83,14 @@ export default function Dashboard() { staleTime: 0, }); + // Anstehende Geburtstage (kommende 30 Tage + vergangene 7) – wie im Cockpit, + // nur für Mitarbeiter/Admins. + const { data: birthdaysData } = useQuery({ + queryKey: ['upcoming-birthdays'], + queryFn: () => birthdayApi.getUpcoming(7, 30), + enabled: !isCustomer, + }); + // Für Kundenportal: Verträge nach eigene/fremd gruppieren const { ownContracts, representedContracts } = useMemo(() => { if (!isCustomerPortal || !contractsData?.data) { @@ -378,6 +390,60 @@ export default function Dashboard() { )} + {/* Anstehende Geburtstage (wie im Cockpit) */} + {birthdaysData?.data && birthdaysData.data.length > 0 && ( + +
+ +

Geburtstage

+ {birthdaysData.data.length} +
+

+ Kunden mit Geburtstag in den nächsten 30 Tagen oder den letzten 7 Tagen. +

+
+ {birthdaysData.data.map((b) => ( +
+
+ +
+ + {b.name} + + ({b.customerNumber}) +

+ {formatDate(b.birthDate)} – wird {b.age} Jahre +

+
+
+
+ {b.isToday ? ( + 🎉 Heute! + ) : b.isPast ? ( + Vor {Math.abs(b.daysUntil)} Tag{Math.abs(b.daysUntil) === 1 ? '' : 'en'} + ) : ( + In {b.daysUntil} Tag{b.daysUntil === 1 ? '' : 'en'} + )} +
+
+ ))} +
+
+ )} + {/* Aufgaben Stats für Mitarbeiter */}