import { useCallback, useEffect, useMemo, useState } from 'react'; import { ActivityIndicator, Pressable, RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native'; import { router } from 'expo-router'; import { fetchStaffTimeSpans, fetchTasks, Task } from '@/src/lib/api'; import { useAuth } from '@/src/providers/auth-provider'; const PRIMARY = '#69c350'; type DashboardData = { tasks: Task[]; openTasks: number; inProgressTasks: number; activeTimeStart: string | null; pendingSubmissions: number; todayMinutes: number; }; function normalizeTaskStatus(value: unknown): 'Offen' | 'In Bearbeitung' | 'Abgeschlossen' { if (value === 'In Bearbeitung') return 'In Bearbeitung'; if (value === 'Abgeschlossen') return 'Abgeschlossen'; return 'Offen'; } function formatMinutes(minutes: number): string { const h = Math.floor(minutes / 60); const m = minutes % 60; return `${h}h ${String(m).padStart(2, '0')}m`; } function formatDateTime(value: string | null): string { if (!value) return '-'; return new Date(value).toLocaleString('de-DE', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit', }); } export default function DashboardScreen() { const { token, user } = useAuth(); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState({ tasks: [], openTasks: 0, inProgressTasks: 0, activeTimeStart: null, pendingSubmissions: 0, todayMinutes: 0, }); const currentUserId = useMemo(() => (user?.id ? String(user.id) : null), [user]); const loadDashboard = useCallback( async (showSpinner = true) => { if (!token || !currentUserId) return; if (showSpinner) setLoading(true); setError(null); try { const [taskRows, spans] = await Promise.all([ fetchTasks(token), fetchStaffTimeSpans(token, currentUserId), ]); const tasks = taskRows || []; const openTasks = tasks.filter((task) => normalizeTaskStatus(task.categorie) === 'Offen').length; const inProgressTasks = tasks.filter( (task) => normalizeTaskStatus(task.categorie) === 'In Bearbeitung' ).length; const activeTime = spans.find((span) => !span.stopped_at) || null; const pendingSubmissions = spans.filter( (span) => (span.state === 'draft' || span.state === 'factual') && !!span.stopped_at ).length; const today = new Date(); const todayIso = today.toISOString().slice(0, 10); const todayMinutes = spans .filter((span) => span.started_at?.slice(0, 10) === todayIso) .reduce((sum, span) => sum + (span.duration_minutes || 0), 0); setData({ tasks, openTasks, inProgressTasks, activeTimeStart: activeTime?.started_at || null, pendingSubmissions, todayMinutes, }); } catch (err) { setError(err instanceof Error ? err.message : 'Dashboard konnte nicht geladen werden.'); } finally { setLoading(false); setRefreshing(false); } }, [currentUserId, token] ); useEffect(() => { if (!token || !currentUserId) return; void loadDashboard(true); }, [currentUserId, loadDashboard, token]); async function onRefresh() { setRefreshing(true); await loadDashboard(false); } return ( }> {error ? {error} : null} {loading ? ( Dashboard wird geladen... ) : null} {!loading ? ( <> Aktive Zeit {data.activeTimeStart ? `Seit ${formatDateTime(data.activeTimeStart)}` : 'Nicht aktiv'} Offene Aufgaben {data.openTasks} In Bearbeitung {data.inProgressTasks} Heute erfasst {formatMinutes(data.todayMinutes)} Zum Einreichen {data.pendingSubmissions} Schnellzugriff router.push('/(tabs)/tasks')}> Aufgaben router.push('/(tabs)/projects')}> Projekten router.push('/(tabs)/time')}> Zeiten router.push('/more/inventory?action=scan')}> Inventar Scan ) : null} ); } const styles = StyleSheet.create({ container: { padding: 16, gap: 12, backgroundColor: '#f9fafb', }, row: { flexDirection: 'row', gap: 10, }, metricCard: { flex: 1, backgroundColor: '#ffffff', borderRadius: 12, borderWidth: 1, borderColor: '#e5e7eb', padding: 12, gap: 6, }, metricCardPrimary: { borderColor: PRIMARY, backgroundColor: '#eff9ea', }, metricLabel: { color: '#6b7280', fontSize: 12, textTransform: 'uppercase', }, metricValue: { color: '#111827', fontSize: 22, fontWeight: '700', }, metricLabelPrimary: { color: '#3d7a30', fontSize: 12, textTransform: 'uppercase', }, metricValuePrimary: { color: '#2f5f24', fontSize: 18, fontWeight: '700', }, quickActionsCard: { backgroundColor: '#ffffff', borderRadius: 12, borderWidth: 1, borderColor: '#e5e7eb', padding: 12, gap: 10, }, quickActionsTitle: { color: '#111827', fontSize: 15, fontWeight: '600', }, quickActionsRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, }, quickActionButton: { minWidth: 120, minHeight: 40, borderRadius: 10, backgroundColor: PRIMARY, alignItems: 'center', justifyContent: 'center', }, quickActionText: { color: '#ffffff', fontWeight: '600', }, error: { color: '#dc2626', fontSize: 13, }, loadingBox: { padding: 16, alignItems: 'center', gap: 8, }, loadingText: { color: '#6b7280', }, });