import React, { useState, useCallback, useContext } from 'react'; import { View, Text, StyleSheet, SectionList, TouchableOpacity, RefreshControl, StatusBar, Alert, Linking } from 'react-native'; import { useFocusEffect } from '@react-navigation/native'; import { AuthContext } from '../context/AuthContext'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import api from '../services/api'; import Colors from '../constants/Colors'; const TasksScreen = ({ navigation }) => { const { userInfo } = useContext(AuthContext); const insets = useSafeAreaInsets(); const [sections, setSections] = useState([]); const [refreshing, setRefreshing] = useState(false); const [activeFilter, setActiveFilter] = useState('ALL'); // ALL, PENDING, DONE const groupByDay = (followups) => { const map = {}; const today = new Date(); today.setHours(0, 0, 0, 0); const tomorrow = new Date(today.getTime() + 86400000); followups.forEach(f => { const d = new Date(f.date); d.setHours(0, 0, 0, 0); let label; if (d.getTime() === today.getTime()) label = 'Today'; else if (d.getTime() < today.getTime()) label = `Overdue — ${d.toLocaleDateString('en-IN', { day: 'numeric', month: 'short' })}`; else if (d.getTime() === tomorrow.getTime()) label = 'Tomorrow'; else label = d.toLocaleDateString('en-IN', { weekday: 'long', day: 'numeric', month: 'short' }); if (!map[label]) map[label] = { title: label, data: [], ts: d.getTime() }; map[label].data.push(f); }); return Object.values(map).sort((a, b) => a.ts - b.ts); }; const fetchTasks = async () => { try { const params = new URLSearchParams({ userId: userInfo.id }); if (activeFilter !== 'ALL') params.append('status', activeFilter); const res = await api.get(`/followups?${params.toString()}`); setSections(groupByDay(res.data)); } catch (e) { console.error('TasksScreen fetch error', e); } finally { setRefreshing(false); } }; useFocusEffect(useCallback(() => { fetchTasks(); }, [activeFilter])); const handleMarkDone = async (id) => { Alert.alert('Mark as Done?', 'This will complete the task and dismiss the notification.', [ { text: 'Cancel', style: 'cancel' }, { text: 'Done ✓', onPress: async () => { try { await api.patch(`/followups/${id}`, { status: 'DONE' }); fetchTasks(); } catch (e) { Alert.alert('Error', 'Could not update task.'); } } } ]); }; const handleCall = (phone) => { if (!phone) return; Linking.openURL(`tel:${phone}`); }; const renderTask = ({ item }) => { const isPending = item.status === 'PENDING'; const isOverdue = isPending && new Date(item.date) < new Date(); return ( {item.client?.name || 'Unknown Client'} {item.client?.phone && ( handleCall(item.client.phone)}> 📞 )} {item.notes} {new Date(item.date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {item.user?.name ? ` • Assigned by ${item.user.name}` : ''} {isPending && ( handleMarkDone(item.id)}> Done )} {!isPending && ( )} ); }; return ( My Tasks Sorted by date {['ALL', 'PENDING', 'DONE'].map(f => ( setActiveFilter(f)} > {f} ))} item.id} renderItem={renderTask} renderSectionHeader={({ section }) => ( {section.title} {section.data.length} task{section.data.length !== 1 ? 's' : ''} )} refreshControl={ { setRefreshing(true); fetchTasks(); }} colors={[Colors.primary]} />} contentContainerStyle={{ paddingBottom: 40 }} ListEmptyComponent={ 🎉 All Clear! No tasks match this filter. } /> ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#f1f5f9' }, header: { backgroundColor: Colors.primary, paddingHorizontal: 20, paddingBottom: 20 }, headerTitle: { color: 'white', fontSize: 26, fontWeight: '900' }, headerSub: { color: 'rgba(255,255,255,0.7)', fontSize: 12, marginTop: 2, marginBottom: 14 }, filterRow: { flexDirection: 'row', gap: 8 }, filterBtn: { paddingHorizontal: 16, paddingVertical: 6, borderRadius: 20, backgroundColor: 'rgba(255,255,255,0.2)' }, filterBtnActive: { backgroundColor: 'white' }, filterText: { color: 'rgba(255,255,255,0.8)', fontSize: 12, fontWeight: '700' }, filterTextActive: { color: Colors.primary }, sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingTop: 20, paddingBottom: 8 }, sectionTitle: { fontSize: 13, fontWeight: '900', color: '#475569', textTransform: 'uppercase', letterSpacing: 0.5 }, sectionCount: { fontSize: 11, color: '#94a3b8', fontWeight: '700' }, card: { backgroundColor: 'white', marginHorizontal: 16, marginBottom: 8, borderRadius: 14, padding: 14, flexDirection: 'row', alignItems: 'center', elevation: 2, shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.06, shadowRadius: 4 }, cardOverdue: { borderLeftWidth: 4, borderLeftColor: '#ef4444' }, cardDone: { opacity: 0.65 }, dot: { width: 10, height: 10, borderRadius: 5, marginRight: 12 }, clientName: { fontSize: 14, fontWeight: '800', color: '#1e293b', marginBottom: 3, flex: 1 }, callIcon: { fontSize: 18, paddingHorizontal: 10 }, notes: { fontSize: 12, color: '#64748b', lineHeight: 17, marginBottom: 5 }, time: { fontSize: 10, color: '#94a3b8', fontWeight: '600' }, doneBtn: { backgroundColor: Colors.primary, paddingHorizontal: 14, paddingVertical: 8, borderRadius: 10, marginLeft: 10 }, doneBtnText: { color: 'white', fontSize: 11, fontWeight: '900' }, completedBadge: { width: 28, height: 28, borderRadius: 14, backgroundColor: '#dcfce7', justifyContent: 'center', alignItems: 'center', marginLeft: 10 }, completedText: { color: '#16a34a', fontWeight: '900', fontSize: 14 }, empty: { alignItems: 'center', paddingTop: 80 }, emptyIcon: { fontSize: 48, marginBottom: 12 }, emptyTitle: { fontSize: 18, fontWeight: '800', color: '#1e293b' }, emptySub: { fontSize: 13, color: '#94a3b8', marginTop: 6 }, }); export default TasksScreen;