import React, { useState, useEffect, useMemo } from 'react' import { Link } from 'react-router-dom' import { CalendarCheck, ClipboardList, FilePenLine, Library } from 'lucide-react' import { useAuth } from '../context/AuthContext' import api from '../utils/api' import { getTenantClubDependencyKey } from '../utils/activeClub' import EmailVerificationBanner from '../components/EmailVerificationBanner' import DashboardTrainingVisibilityWidget from '../components/DashboardTrainingVisibilityWidget' import DashboardOrgInboxWidget from '../components/DashboardOrgInboxWidget' function unitWhenLabel(u) { const d = u.planned_date ? String(u.planned_date).slice(0, 10) : '' const t = u.planned_time_start ? String(u.planned_time_start).slice(0, 5) : '' const bits = [d, t].filter(Boolean) return bits.length ? bits.join(' · ') : 'Termin' } function formatCappedCount(n, capped) { if (capped && n >= 1) return `${n}+` return String(n) } function Dashboard() { const [trainingHome, setTrainingHome] = useState(null) const [phase0Stats, setPhase0Stats] = useState(null) const [dashboardKpisErr, setDashboardKpisErr] = useState(null) const { user, loading: authLoading } = useAuth() const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user]) useEffect(() => { if (!user?.id) { setTrainingHome(null) setPhase0Stats(null) setDashboardKpisErr(null) return undefined } let cancelled = false ;(async () => { setDashboardKpisErr(null) try { const data = await api.getDashboardKpis() if (cancelled || !data || typeof data !== 'object') return const th = data.training_home && typeof data.training_home === 'object' ? data.training_home : {} setTrainingHome({ upcoming: Array.isArray(th.upcoming) ? th.upcoming : [], reviewPending: Array.isArray(th.review_pending) ? th.review_pending : [], plannedWithNotes: Array.isArray(th.planned_with_notes) ? th.planned_with_notes : [], }) setPhase0Stats({ year: data.year, draftCount: data.draft_count, draftCapped: Boolean(data.draft_capped), draftPreview: Array.isArray(data.draft_preview) ? data.draft_preview : [], mineCount: data.mine_count ?? 0, mineCapped: Boolean(data.mine_capped), ytdCompletedCount: data.ytd_completed_count ?? 0, ytdCapped: Boolean(data.ytd_capped), }) } catch (e) { if (!cancelled) { console.error('Dashboard KPIs / Trainingsübersicht:', e) setDashboardKpisErr(e.message || 'Konnte Dashboard-Daten nicht laden') setTrainingHome(null) setPhase0Stats(null) } } })() return () => { cancelled = true } }, [user?.id, tenantClubDepKey]) if (authLoading) { return (

Laden...

) } const draftsHref = '/exercises?status=draft&mine=1' const mineHref = '/exercises?mine=1' return (

Dashboard

Willkommen, {user?.name || user?.email}! Shinkan unterstützt dich bei Übungen, Planung und Vereinsstruktur.

{user ? : null} {user?.id ? ( <>

Kurzüberblick

Trainings dieses Kalenderjahres beziehen sich auf den geplanten Termin (nicht zwingend Abschlussdatum). Zahlen können bei sehr vielen Einträgen mit „+“ enden.

{dashboardKpisErr ? (

{dashboardKpisErr}

) : null} {!dashboardKpisErr && !phase0Stats ? (
Zahlen werden geladen…
) : null} {!dashboardKpisErr && phase0Stats ? (
{formatCappedCount(phase0Stats.draftCount, phase0Stats.draftCapped)} Übungs-Entwürfe finalisieren {formatCappedCount(phase0Stats.mineCount, phase0Stats.mineCapped)} Meine Übungen alle Status
{formatCappedCount(phase0Stats.ytdCompletedCount, phase0Stats.ytdCapped)} Gehalten {phase0Stats.year} abrechnungsnah
) : null} {!dashboardKpisErr && phase0Stats?.draftPreview?.length ? (

Entwürfe fertigstellen

Private Übungs-Entwürfe (z. B. aus der Planung) — Ziel, Durchführung und Details in der Bearbeitung ergänzen.

    {phase0Stats.draftPreview.map((ex) => (
  • {ex.title}
  • ))}

Alle Entwürfe in der Übersicht

) : null}

Trainings

Einheiten, bei denen du als Leitung oder Co-Trainer eingetragen bist.

Planung

Nächste Termine

{dashboardKpisErr ? (

{dashboardKpisErr}

) : trainingHome?.upcoming?.length ? (
    {trainingHome.upcoming.map((u) => (
  • {unitWhenLabel(u)} {u.group_name ? ( {` — ${u.group_name}`} ) : null} {u.lead_trainer_name ? ( Leitung: {u.lead_trainer_name} ) : null}
  • ))}
) : (

Keine anstehenden Termine.{' '} Zur Trainingsplanung

)}

Hinweise (anstehend)

{dashboardKpisErr ? (

{dashboardKpisErr}

) : trainingHome?.plannedWithNotes?.length ? (
    {trainingHome.plannedWithNotes.map((u) => { const snippet = (u.trainer_notes || u.notes || '').trim().slice(0, 120) return (
  • {unitWhenLabel(u)} {u.group_name ? ( {` · ${u.group_name}`} ) : null}
    {snippet} {(u.trainer_notes || u.notes || '').trim().length > 120 ? '…' : ''}
  • ) })}
) : (

Keine Vermerke in den nächsten geplanten Terminen.

)}

Offene Rückschau

{dashboardKpisErr ? (

{dashboardKpisErr}

) : trainingHome?.reviewPending?.length ? (
    {trainingHome.reviewPending.map((u) => (
  • {(u.actual_date || u.planned_date || '').toString().slice(0, 10) || 'Datum'} {u.group_name ? ( {` — ${u.group_name}`} ) : null}
  • ))}
) : (

Keine durchgeführten Trainings mit offener Nachbereitung. Zum Abschluss der Rückschau in der Planung „Rückschau erledigt“ aktivieren.

)}
) : null}
) } export default Dashboard