changes till 12/06/2026

changes till 12/06/2026
main
Manu Krishna 2026-06-12 09:56:27 +05:30
parent 69f1ee64fb
commit 7e735e3e7d
2 changed files with 292 additions and 146 deletions

View File

@ -1,7 +1,7 @@
// Environment Configuration // Environment Configuration
const ENV = { const ENV = {
dev: { dev: {
API_URL: 'http://192.168.65.3:3000', // Local Dev IP API_URL: 'http://192.168.50.27:3004', // Local Dev IP
}, },
prod: { prod: {
API_URL: 'https://crmapi.ignosimoney.in', // Change this to your public IP/Domain API_URL: 'https://crmapi.ignosimoney.in', // Change this to your public IP/Domain

View File

@ -1,7 +1,7 @@
import React, { useState, useCallback, useContext } from 'react'; import React, { useState, useCallback, useContext } from 'react';
import { import {
View, Text, StyleSheet, SectionList, TouchableOpacity, View, Text, StyleSheet, FlatList, TouchableOpacity,
RefreshControl, StatusBar, Alert, Linking RefreshControl, StatusBar, Alert, Linking, TextInput, ScrollView
} from 'react-native'; } from 'react-native';
import { useFocusEffect } from '@react-navigation/native'; import { useFocusEffect } from '@react-navigation/native';
import { AuthContext } from '../context/AuthContext'; import { AuthContext } from '../context/AuthContext';
@ -19,7 +19,7 @@ const TYPE_ICONS = {
'VISIT_SCHEDULED': '📍', 'VISIT_SCHEDULED': '📍',
'VISIT_COMPLETED': '🏁', 'VISIT_COMPLETED': '🏁',
'NEGOTIATION': '🤝', 'NEGOTIATION': '🤝',
'FOLLOWUP': '📅', 'FOLLOWUP': '📌',
'DEMO': '📽️', 'DEMO': '📽️',
'QUOTE': '📝', 'QUOTE': '📝',
}; };
@ -39,41 +39,68 @@ const TYPE_COLORS = {
'QUOTE': '#a855f7', 'QUOTE': '#a855f7',
}; };
const MANDATORY_TYPES = ['DEMO_COMPLETED', 'VISIT_COMPLETED', 'DEMO'];
const CATEGORIES = [
{ key: 'today', label: "Today's Focus", color: '#16a34a', bg: '#dcfce7' },
{ key: 'tomorrow', label: 'Tomorrow', color: '#d97706', bg: '#fef3c7' },
{ key: 'thisWeek', label: 'Later This Week', color: '#2563eb', bg: '#dbeafe' },
{ key: 'upcoming', label: 'Upcoming', color: '#475569', bg: '#f1f5f9' },
{ key: 'expired', label: 'Expired Follow-ups', color: '#dc2626', bg: '#fee2e2' },
];
const startOfDay = (d) => { const x = new Date(d); x.setHours(0, 0, 0, 0); return x; };
const endOfDay = (d) => { const x = new Date(d); x.setHours(23, 59, 59, 999); return x; };
const categorizeActivities = (followups) => {
const now = new Date();
const todayStart = startOfDay(now);
const todayEnd = endOfDay(now);
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);
const tomorrowStart = startOfDay(tomorrow);
const tomorrowEnd = endOfDay(tomorrow);
// End of current ISO week (Sunday)
const weekEnd = new Date(todayStart);
const day = weekEnd.getDay(); // 0=Sun..6=Sat
const daysToSunday = day === 0 ? 0 : 7 - day;
weekEnd.setDate(weekEnd.getDate() + daysToSunday);
weekEnd.setHours(23, 59, 59, 999);
const result = { today: [], tomorrow: [], thisWeek: [], upcoming: [], expired: [] };
followups.forEach(f => {
if (f.status === 'DONE' || f.status === 'EXPIRED') return;
const d = new Date(f.date);
if (d < todayStart) result.expired.push(f);
else if (d <= todayEnd) result.today.push(f);
else if (d <= tomorrowEnd) result.tomorrow.push(f);
else if (d <= weekEnd) result.thisWeek.push(f);
else result.upcoming.push(f);
});
return result;
};
const TasksScreen = ({ navigation }) => { const TasksScreen = ({ navigation }) => {
const { userInfo } = useContext(AuthContext); const { userInfo } = useContext(AuthContext);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const [sections, setSections] = useState([]);
const [allActivities, setAllActivities] = useState([]);
const [categorized, setCategorized] = useState({ today: [], tomorrow: [], thisWeek: [], upcoming: [], expired: [] });
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [activeFilter, setActiveFilter] = useState('PENDING'); // ALL, PENDING, DONE const [activeCategory, setActiveCategory] = useState('today');
const [inlineRemarks, setInlineRemarks] = useState({});
const groupByDay = (followups) => { const [submitting, setSubmitting] = useState(null); // holds activity id being submitted
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 () => { const fetchTasks = async () => {
try { try {
const params = new URLSearchParams({ userId: userInfo.id }); const params = new URLSearchParams({ userId: userInfo.id, status: 'PENDING' });
if (activeFilter !== 'ALL') params.append('status', activeFilter);
const res = await api.get(`/followups?${params.toString()}`); const res = await api.get(`/followups?${params.toString()}`);
setSections(groupByDay(res.data)); setAllActivities(res.data);
setCategorized(categorizeActivities(res.data));
} catch (e) { } catch (e) {
console.error('TasksScreen fetch error', e); console.error('TasksScreen fetch error', e);
} finally { } finally {
@ -81,74 +108,111 @@ const TasksScreen = ({ navigation }) => {
} }
}; };
useFocusEffect(useCallback(() => { fetchTasks(); }, [activeFilter])); useFocusEffect(useCallback(() => { fetchTasks(); }, []));
const handleMarkDone = async (item) => {
const isMandatory = ['DEMO_COMPLETED', 'VISIT_COMPLETED', 'DEMO'].includes(item.type);
if (isMandatory) {
navigation.navigate('Feedback', { activity: item });
return;
}
Alert.alert('Complete Activity?', 'This will mark the activity as done and remove it from pending.', [
{ text: 'Cancel', style: 'cancel' },
{
text: 'Complete ✓', onPress: async () => {
try {
await api.patch(`/followups/${item.id}`, { status: 'DONE' });
fetchTasks();
} catch (e) {
Alert.alert('Error', 'Could not update activity.');
}
}
}
]);
};
const handleCall = (phone) => { const handleCall = (phone) => {
if (!phone) return; if (!phone) return;
Linking.openURL(`tel:${phone}`); Linking.openURL(`tel:${phone}`);
}; };
const renderTask = ({ item }) => { const handleInlineMarkDone = async (item) => {
const isPending = item.status === 'PENDING'; if (MANDATORY_TYPES.includes(item.type)) {
const isOverdue = isPending && new Date(item.date) < new Date(); navigation.navigate('Feedback', { activity: item });
return;
}
setSubmitting(item.id);
try {
const payload = { status: 'DONE' };
const remarks = inlineRemarks[item.id];
if (remarks && remarks.trim()) payload.notes = remarks.trim();
await api.patch(`/followups/${item.id}`, payload);
setInlineRemarks(prev => {
const updated = { ...prev };
delete updated[item.id];
return updated;
});
fetchTasks();
} catch (e) {
Alert.alert('Error', 'Could not update activity.');
} finally {
setSubmitting(null);
}
};
const currentCat = CATEGORIES.find(c => c.key === activeCategory);
const currentItems = categorized[activeCategory] || [];
const renderItem = ({ item }) => {
const type = item.type || 'FOLLOWUP'; const type = item.type || 'FOLLOWUP';
const isMandatory = MANDATORY_TYPES.includes(type);
return ( return (
<View style={[styles.card, isOverdue && styles.cardOverdue, !isPending && styles.cardDone]}> <View style={[styles.card, { borderLeftColor: currentCat.color }]}>
<View style={[styles.typeIconBadge, { backgroundColor: TYPE_COLORS[type] + '20' }]}> {/* Card Header */}
<Text style={styles.typeIconText}>{TYPE_ICONS[type]}</Text> <View style={styles.cardHeader}>
</View> <View style={[styles.typeIconBadge, { backgroundColor: (TYPE_COLORS[type] || '#64748b') + '20' }]}>
<View style={{ flex: 1 }}> <Text style={styles.typeIconText}>{TYPE_ICONS[type] || '📌'}</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}> </View>
<Text style={styles.clientName}>{item.client?.companyName || item.client?.name || 'Unknown Client'}</Text> <View style={{ flex: 1 }}>
{item.client?.phone && ( <Text style={styles.clientName} numberOfLines={1}>
<TouchableOpacity onPress={() => handleCall(item.client.phone)} style={styles.callCircle}> {item.client?.companyName || item.client?.name || 'Unknown Client'}
<Text style={styles.callIcon}>📞</Text> </Text>
</TouchableOpacity> <View style={styles.typeBadgeRow}>
<View style={[styles.typeBadge, { backgroundColor: (TYPE_COLORS[type] || '#64748b') + '15' }]}>
<Text style={[styles.typeText, { color: TYPE_COLORS[type] || '#64748b' }]}>
{type.replace(/_/g, ' ')}
</Text>
</View>
<Text style={styles.timeText}>
{new Date(item.date).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' })}
{' '}
{new Date(item.date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</Text>
</View>
{item.opportunity && (
<Text style={styles.oppText} numberOfLines={1}>💼 {item.opportunity.title}</Text>
)} )}
</View> </View>
<View style={styles.typeBadge}> {item.client?.phone && (
<Text style={[styles.typeText, { color: TYPE_COLORS[type] || '#64748b' }]}>{type.replace('_', ' ')}</Text> <TouchableOpacity onPress={() => handleCall(item.client.phone)} style={styles.callCircle}>
</View> <Text style={styles.callIcon}>📞</Text>
<Text style={styles.notes} numberOfLines={2}>{item.notes}</Text> </TouchableOpacity>
<Text style={styles.time}> )}
{new Date(item.date).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
{item.user?.name ? ` • Assigned by ${item.user.name}` : ''}
</Text>
</View> </View>
{isPending && (
<TouchableOpacity style={[styles.doneBtn, { backgroundColor: TYPE_COLORS[type] }]} onPress={() => handleMarkDone(item.id)}> {/* Notes */}
<Text style={styles.doneBtnText}>Done</Text> {!!item.notes && (
<Text style={styles.notes} numberOfLines={2}>"{item.notes}"</Text>
)}
{/* Assigned user */}
<Text style={styles.assignedText}>
👤 {item.user?.name || 'Unassigned'}
</Text>
{/* Inline Remarks + Mark Done */}
<View style={styles.inlineRow}>
<TextInput
style={[styles.remarksInput, isMandatory && styles.remarksInputDisabled]}
placeholder={isMandatory ? 'Needs feedback modal →' : 'Add remarks...'}
placeholderTextColor={isMandatory ? '#cbd5e1' : '#94a3b8'}
value={inlineRemarks[item.id] || ''}
onChangeText={v => setInlineRemarks(prev => ({ ...prev, [item.id]: v }))}
editable={!isMandatory}
/>
<TouchableOpacity
style={[styles.doneBtn, { backgroundColor: currentCat.color }, submitting === item.id && { opacity: 0.6 }]}
onPress={() => handleInlineMarkDone(item)}
disabled={submitting === item.id}
>
<Text style={styles.doneBtnText}>
{isMandatory ? '📋 Open' : submitting === item.id ? '...' : '✓ Done'}
</Text>
</TouchableOpacity> </TouchableOpacity>
)} </View>
{!isPending && (
<View style={styles.completedBadge}>
<Text style={styles.completedText}></Text>
</View>
)}
</View> </View>
); );
}; };
@ -156,11 +220,13 @@ const TasksScreen = ({ navigation }) => {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<StatusBar backgroundColor={Colors.primary} barStyle="light-content" /> <StatusBar backgroundColor={Colors.primary} barStyle="light-content" />
{/* Header */}
<View style={[styles.header, { paddingTop: insets.top + 16 }]}> <View style={[styles.header, { paddingTop: insets.top + 16 }]}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}> <View style={styles.headerTop}>
<View> <View>
<Text style={styles.headerTitle}>Activities</Text> <Text style={styles.headerTitle}>Activities</Text>
<Text style={styles.headerSub}>Manage your schedule</Text> <Text style={styles.headerSub}>Daily follow-up engine</Text>
</View> </View>
<TouchableOpacity <TouchableOpacity
style={styles.addBtn} style={styles.addBtn}
@ -169,36 +235,73 @@ const TasksScreen = ({ navigation }) => {
<Text style={styles.addBtnText}>+ New</Text> <Text style={styles.addBtnText}>+ New</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.filterRow}>
{['PENDING', 'DONE', 'ALL'].map(f => ( {/* Category Picker */}
<TouchableOpacity <ScrollView
key={f} horizontal
style={[styles.filterBtn, activeFilter === f && styles.filterBtnActive]} showsHorizontalScrollIndicator={false}
onPress={() => setActiveFilter(f)} style={styles.categoryScroll}
> contentContainerStyle={styles.categoryScrollContent}
<Text style={[styles.filterText, activeFilter === f && styles.filterTextActive]}>{f}</Text> >
</TouchableOpacity> {CATEGORIES.map(cat => {
))} const count = categorized[cat.key]?.length || 0;
</View> const isActive = activeCategory === cat.key;
return (
<TouchableOpacity
key={cat.key}
style={[
styles.catBtn,
isActive && { backgroundColor: 'white' },
]}
onPress={() => setActiveCategory(cat.key)}
>
<Text style={[styles.catBtnText, isActive && { color: cat.color }]}>
{cat.label}
</Text>
<View style={[styles.catCount, { backgroundColor: isActive ? cat.color : 'rgba(255,255,255,0.25)' }]}>
<Text style={styles.catCountText}>{count}</Text>
</View>
</TouchableOpacity>
);
})}
</ScrollView>
</View> </View>
<SectionList {/* Section Header */}
sections={sections} <View style={[styles.sectionBanner, { backgroundColor: currentCat.bg, borderLeftColor: currentCat.color }]}>
<Text style={[styles.sectionBannerTitle, { color: currentCat.color }]}>
{currentCat.label}
</Text>
<Text style={[styles.sectionBannerCount, { color: currentCat.color }]}>
{currentItems.length} {currentItems.length === 1 ? 'activity' : 'activities'}
</Text>
</View>
<FlatList
data={currentItems}
keyExtractor={item => item.id} keyExtractor={item => item.id}
renderItem={renderTask} renderItem={renderItem}
renderSectionHeader={({ section }) => ( refreshControl={
<View style={styles.sectionHeader}> <RefreshControl
<Text style={styles.sectionTitle}>{section.title}</Text> refreshing={refreshing}
<Text style={styles.sectionCount}>{section.data.length} item{section.data.length !== 1 ? 's' : ''}</Text> onRefresh={() => { setRefreshing(true); fetchTasks(); }}
</View> colors={[Colors.primary]}
)} />
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); fetchTasks(); }} colors={[Colors.primary]} />} }
contentContainerStyle={{ paddingBottom: 40 }} contentContainerStyle={{ paddingHorizontal: 16, paddingTop: 12, paddingBottom: 40 }}
ListEmptyComponent={ ListEmptyComponent={
<View style={styles.empty}> <View style={styles.empty}>
<Text style={styles.emptyIcon}></Text> <Text style={styles.emptyIcon}>
<Text style={styles.emptyTitle}>All Caught Up!</Text> {activeCategory === 'expired' ? '🎉' : '✨'}
<Text style={styles.emptySub}>No activities scheduled here.</Text> </Text>
<Text style={styles.emptyTitle}>
{activeCategory === 'expired' ? 'No expired follow-ups!' : 'All clear here!'}
</Text>
<Text style={styles.emptySub}>
{activeCategory === 'expired'
? 'You\'re on top of everything.'
: 'No activities in this category.'}
</Text>
</View> </View>
} }
/> />
@ -208,39 +311,82 @@ const TasksScreen = ({ navigation }) => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f8fafc' }, container: { flex: 1, backgroundColor: '#f8fafc' },
header: { backgroundColor: Colors.primary, paddingHorizontal: 20, paddingBottom: 20 },
headerTitle: { color: 'white', fontSize: 28, fontWeight: '900' }, // Header
headerSub: { color: 'rgba(255,255,255,0.7)', fontSize: 13, marginTop: 2, marginBottom: 16 }, header: { backgroundColor: Colors.primary, paddingHorizontal: 20, paddingBottom: 12 },
filterRow: { flexDirection: 'row', gap: 8 }, headerTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 },
filterBtn: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 20, backgroundColor: 'rgba(255,255,255,0.2)' }, headerTitle: { color: 'white', fontSize: 26, fontWeight: '900' },
filterBtnActive: { backgroundColor: 'white' }, headerSub: { color: 'rgba(255,255,255,0.7)', fontSize: 12, marginTop: 2 },
filterText: { color: 'rgba(255,255,255,0.8)', fontSize: 12, fontWeight: '800' }, addBtn: { backgroundColor: 'rgba(255,255,255,0.2)', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 12 },
filterTextActive: { color: Colors.primary },
addBtn: { backgroundColor: 'rgba(255,255,255,0.25)', paddingHorizontal: 14, paddingVertical: 8, borderRadius: 12 },
addBtnText: { color: 'white', fontWeight: '900', fontSize: 13 }, addBtnText: { color: 'white', fontWeight: '900', fontSize: 13 },
sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingTop: 24, paddingBottom: 10 },
sectionTitle: { fontSize: 12, fontWeight: '900', color: '#64748b', textTransform: 'uppercase', letterSpacing: 1 }, // Category picker
sectionCount: { fontSize: 11, color: '#94a3b8', fontWeight: '700' }, categoryScroll: { marginBottom: 4 },
card: { backgroundColor: 'white', marginHorizontal: 16, marginBottom: 10, borderRadius: 18, padding: 16, flexDirection: 'row', alignItems: 'center', elevation: 3, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 8 }, categoryScrollContent: { gap: 8, paddingRight: 4 },
cardOverdue: { borderLeftWidth: 5, borderLeftColor: '#ef4444' }, catBtn: {
cardDone: { opacity: 0.7 }, flexDirection: 'row', alignItems: 'center', gap: 6,
typeIconBadge: { width: 44, height: 44, borderRadius: 14, alignItems: 'center', justifyContent: 'center', marginRight: 14 }, paddingHorizontal: 12, paddingVertical: 7,
typeIconText: { fontSize: 20 }, borderRadius: 20, backgroundColor: 'rgba(255,255,255,0.18)',
clientName: { fontSize: 15, fontWeight: '800', color: '#1e293b', marginBottom: 2, flex: 1 }, },
callCircle: { width: 32, height: 32, borderRadius: 16, backgroundColor: '#f1f5f9', alignItems: 'center', justifyContent: 'center' }, catBtnText: { fontSize: 11, fontWeight: '800', color: 'rgba(255,255,255,0.85)' },
callIcon: { fontSize: 14 }, catCount: { minWidth: 18, height: 18, borderRadius: 9, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 4 },
typeBadge: { alignSelf: 'flex-start', paddingHorizontal: 8, paddingVertical: 2, borderRadius: 6, backgroundColor: '#f8fafc', marginBottom: 6 }, catCountText: { color: 'white', fontSize: 10, fontWeight: '900' },
// Section Banner
sectionBanner: {
flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center',
marginHorizontal: 16, marginTop: 12, marginBottom: 4,
paddingHorizontal: 14, paddingVertical: 10,
borderRadius: 10, borderLeftWidth: 4,
},
sectionBannerTitle: { fontSize: 12, fontWeight: '900', textTransform: 'uppercase', letterSpacing: 0.8 },
sectionBannerCount: { fontSize: 11, fontWeight: '700' },
// Cards
card: {
backgroundColor: 'white',
borderRadius: 14, marginBottom: 12,
padding: 14,
borderLeftWidth: 4,
elevation: 2,
shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.06, shadowRadius: 6,
},
cardHeader: { flexDirection: 'row', alignItems: 'flex-start', gap: 10, marginBottom: 8 },
typeIconBadge: { width: 40, height: 40, borderRadius: 12, alignItems: 'center', justifyContent: 'center', marginTop: 2 },
typeIconText: { fontSize: 18 },
clientName: { fontSize: 14, fontWeight: '800', color: '#1e293b', marginBottom: 4 },
typeBadgeRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
typeBadge: { paddingHorizontal: 7, paddingVertical: 2, borderRadius: 6 },
typeText: { fontSize: 10, fontWeight: '900', textTransform: 'uppercase' }, typeText: { fontSize: 10, fontWeight: '900', textTransform: 'uppercase' },
notes: { fontSize: 13, color: '#475569', lineHeight: 18, marginBottom: 8 }, timeText: { fontSize: 10, color: '#94a3b8', fontWeight: '600' },
time: { fontSize: 11, color: '#94a3b8', fontWeight: '600' }, oppText: { fontSize: 11, color: '#64748b', marginTop: 4, fontStyle: 'italic' },
doneBtn: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 12, marginLeft: 12 }, callCircle: { width: 34, height: 34, borderRadius: 17, backgroundColor: '#f1f5f9', alignItems: 'center', justifyContent: 'center', marginLeft: 4 },
doneBtnText: { color: 'white', fontSize: 12, fontWeight: '900' }, callIcon: { fontSize: 15 },
completedBadge: { width: 32, height: 32, borderRadius: 16, backgroundColor: '#dcfce7', justifyContent: 'center', alignItems: 'center', marginLeft: 12 },
completedText: { color: '#16a34a', fontWeight: '900', fontSize: 16 }, notes: { fontSize: 12, color: '#475569', fontStyle: 'italic', marginBottom: 6, lineHeight: 17 },
empty: { alignItems: 'center', paddingTop: 100 }, assignedText: { fontSize: 11, color: '#94a3b8', fontWeight: '600', marginBottom: 10 },
emptyIcon: { fontSize: 56, marginBottom: 16 },
emptyTitle: { fontSize: 20, fontWeight: '900', color: '#1e293b' }, // Inline action row
emptySub: { fontSize: 14, color: '#64748b', marginTop: 8 }, inlineRow: { flexDirection: 'row', alignItems: 'center', gap: 8 },
remarksInput: {
flex: 1, height: 36,
borderWidth: 1, borderColor: '#e2e8f0',
borderRadius: 8, paddingHorizontal: 10,
fontSize: 12, color: '#1e293b',
backgroundColor: '#f8fafc',
},
remarksInputDisabled: { backgroundColor: '#f1f5f9', color: '#94a3b8' },
doneBtn: {
paddingHorizontal: 12, paddingVertical: 8,
borderRadius: 8,
},
doneBtnText: { color: 'white', fontSize: 11, fontWeight: '900' },
// Empty
empty: { alignItems: 'center', paddingTop: 80 },
emptyIcon: { fontSize: 52, marginBottom: 14 },
emptyTitle: { fontSize: 18, fontWeight: '900', color: '#1e293b' },
emptySub: { fontSize: 13, color: '#64748b', marginTop: 6, textAlign: 'center' },
}); });
export default TasksScreen; export default TasksScreen;