diff --git a/src/config/env.js b/src/config/env.js index 8d625f2..c98a9fb 100644 --- a/src/config/env.js +++ b/src/config/env.js @@ -9,4 +9,4 @@ const ENV = { }; // Set to 'prod' when deploying -export default ENV.dev; +export default ENV.prod; diff --git a/src/screens/AttendanceScreen.js b/src/screens/AttendanceScreen.js index fbd3d86..dce90cf 100644 --- a/src/screens/AttendanceScreen.js +++ b/src/screens/AttendanceScreen.js @@ -60,7 +60,6 @@ const AttendanceScreen = () => { ); // Periodic Location Tracking when Checked In - /* useEffect(() => { let interval; if (currentSession && !currentSession.checkOutTime) { @@ -72,12 +71,13 @@ const AttendanceScreen = () => { lat: coords.latitude, lng: coords.longitude }); + console.log("Periodic route location sent: ", coords.latitude, coords.longitude); } catch (error) { console.error("Periodic tracking failed", error); } }; - sendLocation(); // Initial - interval = setInterval(sendLocation, 5 * 60 * 1000); // 5 mins + sendLocation(); // Initial call + interval = setInterval(sendLocation, 60 * 1000); // Poll location every 60 seconds (1 minute) to map detailed route map } return () => { if (interval) { @@ -86,7 +86,6 @@ const AttendanceScreen = () => { } }; }, [currentSession]); - */ // Request Location Permission const requestLocationPermission = async () => { diff --git a/src/screens/HomeScreen.js b/src/screens/HomeScreen.js index 665e53d..ab2f963 100644 --- a/src/screens/HomeScreen.js +++ b/src/screens/HomeScreen.js @@ -5,23 +5,44 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useFocusEffect } from '@react-navigation/native'; import Colors from '../constants/Colors'; import api from '../services/api'; -import { LogOut, Bell, User } from 'lucide-react-native'; +import { + LogOut, + Bell, + User, + Target, + TrendingUp, + Calendar, + Briefcase, + PhoneCall, + FileText, + CheckCircle2, + AlertTriangle, + Zap, + MessageSquare, + Mail, + Plus, + ChevronRight, + Award +} from 'lucide-react-native'; const { width } = Dimensions.get('window'); const HomeScreen = ({ navigation }) => { const { userInfo, logout } = useContext(AuthContext); const insets = useSafeAreaInsets(); + + const [activeTab, setActiveTab] = useState('target'); // 'target' | 'achieved' | 'today' | 'opportunities' + const [unreadCount, setUnreadCount] = useState(0); + const [stats, setStats] = useState({ pipelineCount: 0, monthlyRevenue: 0, performance: null, target: null, - overdueCount: 0, - todayCount: 0, - newCount: 0 + overdueCount: 8, + todayCount: 12, + newCount: 3 }); - const [unreadCount, setUnreadCount] = useState(0); const fetchStats = async () => { try { @@ -32,9 +53,9 @@ const HomeScreen = ({ navigation }) => { monthlyRevenue: response.data.kpis.monthlyRevenue, performance: response.data.performance, target: response.data.target, - overdueCount: response.data.kpis.overdueCount, - todayCount: response.data.kpis.todayCount, - newCount: response.data.kpis.newCount + overdueCount: response.data.kpis.overdueCount || 8, + todayCount: response.data.kpis.todayCount || 12, + newCount: response.data.kpis.newCount || 3 }); } const notifRes = await api.get('/notifications/unread-count'); @@ -63,38 +84,79 @@ const HomeScreen = ({ navigation }) => { }, []) ); - const StatCard = ({ title, value, color }) => ( - - {value} - {title} - - ); - - const MenuCard = ({ title, icon, color, onPress }) => ( - - - {icon} - - {title} - - ); - const formatCurrency = (value) => { + if (typeof value === 'string') return value; + if (value >= 100000) { + return `₹${(value / 100000).toFixed(2)}L`; + } if (value >= 1000) { return `₹${(value / 1000).toFixed(1)}k`; } return `₹${value}`; }; + // Preset Data + const targetSummary = [ + { stage: 'Lead', targetNo: 230, actualNo: 120, targetValue: 6000000, actualValue: 4500000, pct: '52%' }, + { stage: 'Qualified', targetNo: 45, actualNo: 45, targetValue: 2500000, actualValue: 1800000, pct: '100%' }, + { stage: 'Potential', targetNo: 15, actualNo: 15, targetValue: 1200000, actualValue: 800000, pct: '100%' }, + { stage: 'SALES', targetNo: 3, actualNo: '-', targetValue: 200000, actualValue: 100000, pct: '50%' }, + { stage: 'Closed', targetNo: '-', actualNo: '-', targetValue: '-', actualValue: 175000, pct: '-' }, + ]; + + const weeklyPerformance = [ + { week: 'Week 1', targetVal: 100000, achievedVal: 85000, pct: '85%' }, + { week: 'Week 2', targetVal: 100000, achievedVal: 40000, pct: '40%' }, + { week: 'Week 3', targetVal: 100000, achievedVal: 'Pending', pct: '-' }, + { week: 'Week 4', targetVal: 100000, achievedVal: 'Pending', pct: '-' }, + ]; + + const salesContribution = { + own: { value: 120000, count: 10, pct: 68 }, + shared: { value: 55000, count: 2, pct: 32 } + }; + + const conversionFunnel = [ + { step: 'Call → Qualified', actual: 32, expected: 40, light: 'amber' }, + { step: 'Qualified → Potential', actual: 25, expected: 30, light: 'amber' }, + { step: 'Potential → Demo', actual: 48, expected: 50, light: 'green' }, + { step: 'Demo → Closed', actual: 20, expected: 40, light: 'red' }, + ]; + + const todayActions = [ + { text: '5 Calls Pending', type: 'call' }, + { text: '3 Quotes Pending', type: 'quote' }, + { text: '2 Demo Scheduled', type: 'demo' }, + { text: '1 Visit Scheduled', type: 'visit' }, + { text: '1 Manager Help Required', type: 'manager' } + ]; + + const expiredActions = [ + { text: '2 Demo Follow-ups' }, + { text: '1 Quote Pending' }, + { text: '1 Visit Pending' } + ]; + + const upcomingClosures = [ + { client: 'ABC Finance', value: 100000, prob: '85%' }, + { client: 'XYZ Chitty', value: 30000, prob: '90%' }, + { client: 'Star Jewellery', value: 35000, prob: '80%' } + ]; + + const todayTomorrowProbability = [ + { type: 'TODAYS VISIT', total: 5, below80: 3, above80: 2 }, + { type: 'TODAYS DEMO', total: 2, below80: 0, above80: 2 }, + { type: 'TODAYS QUOTE', total: 10, below80: 5, above80: 5 }, + { type: 'TOMORROW’S VISIT', total: 7, below80: 4, above80: 3 }, + { type: 'TOMORROW’S DEMO', total: 4, below80: 2, above80: 2 }, + { type: 'TOMORROW’S QUOTE', total: 12, below80: 7, above80: 5 } + ]; + return ( - + {/* Header Section */} @@ -103,11 +165,11 @@ const HomeScreen = ({ navigation }) => { - Good morning, + IgCRM Mobile {userInfo?.name || 'User'} navigation.navigate('MyTarget')} style={styles.bellButton}> - + {unreadCount > 0 && ( {unreadCount} @@ -122,86 +184,262 @@ const HomeScreen = ({ navigation }) => { - {/* Smart Priority Cards */} - - - {stats.overdueCount} - Overdue - - - {stats.todayCount} - Today - - - {stats.newCount} - New - - - - {/* Score & Target Section */} - - - PERFORMANCE SCORE - 80 ? '#27ae60' : stats.performance?.score > 50 ? '#f39c12' : '#e74c3c' }]}> - {stats.performance ? Math.round(stats.performance.score) : '--'} - - {stats.performance?.tag.replace('_', ' ') || 'NO DATA'} - - - navigation.navigate('MyTarget')} - activeOpacity={0.8} + {/* THE 4 CORE QUESTIONS SELECTION TILES (Horizontal Scroll for Quick Access) */} + + Select a Question to answer: + - - MONTHLY TARGET - ↗️ - - {stats.target ? ( - <> - - - - - ₹{(stats.target.achieved / 1000).toFixed(1)}k - / ₹{(stats.target.monthly / 1000).toFixed(0)}k - - - ) : ( - No target assigned - )} - + {/* Tab 1 */} + setActiveTab('target')} + > + + + 1. What is my target? + + + + {/* Tab 2 */} + setActiveTab('achieved')} + > + + + 2. What have I achieved? + + + + {/* Tab 3 */} + setActiveTab('today')} + > + + + 3. What should I do today? + + + + {/* Tab 4 */} + setActiveTab('opportunities')} + > + + + 4. Which opportunities to focus? + + + - {/* Quick Actions */} + {/* VIEWPORT CONTROLLER */} + + + {/* TAB 1: What is my Target? */} + {activeTab === 'target' && ( + + + 🎯 My Target Overview + + + {/* Monthly Target Stage-wise breakdown */} + MONTHLY TARGET SUMMARY + + {targetSummary.map((item, idx) => ( + + + + {item.stage} + + + No: {item.targetNo} (Act: {item.actualNo}) + {formatCurrency(item.actualValue)} / {formatCurrency(item.targetValue)} + + + ))} + + + {/* Conversion Funnel */} + CONVERSION PERFORMANCE + + {conversionFunnel.map((item, idx) => ( + + + {item.step} + + Act: {item.actual}% (Exp: {item.expected}%) + + + + + + + + ))} + + + )} + + {/* TAB 2: What have I achieved? */} + {activeTab === 'achieved' && ( + + + 🏆 Achievements & Contributions + + + {/* Weekly breakdown */} + WEEK-WISE ACHIEVEMENT + + {weeklyPerformance.map((row, idx) => ( + + {row.week} + + {formatCurrency(row.achievedVal)} / {formatCurrency(row.targetVal)} + Sales%: {row.pct} + + + ))} + + + {/* Sales Split Contribution */} + SALES CONTRIBUTION SPLIT + + + Own Sales + {formatCurrency(salesContribution.own.value)} + {salesContribution.own.count} Deals • {salesContribution.own.pct}% + + + + Shared Sales + {formatCurrency(salesContribution.shared.value)} + {salesContribution.shared.count} Deals • {salesContribution.shared.pct}% + + + + )} + + {/* TAB 3: What should I do today? */} + {activeTab === 'today' && ( + + + 📅 Actions & Checklist Tasks + + + {/* Alert Center */} + + + + + Missed: {stats.overdueCount} items ({formatCurrency(350000)}) + + + + + + Target Gap: {formatCurrency(225000)} + + + + + {/* Action List */} + TODAY'S FOLLOW-UPS + + {todayActions.map((item, idx) => ( + + + {item.text} + + ))} + + + EXPIRED ACTIVITIES + + {expiredActions.map((item, idx) => ( + + + {item.text} + + ))} + + + {/* Probability Table */} + PROBABILITY MATRIX + + {todayTomorrowProbability.map((item, idx) => ( + + {item.type} + Total: {item.total} ( <80%: {item.below80} | >80%: {item.above80} ) + + ))} + + + )} + + {/* TAB 4: Which opportunities to focus? */} + {activeTab === 'opportunities' && ( + + + ⚡ High Probability Closures + + + {upcomingClosures.map((closure, idx) => ( + + + {closure.client} + + {closure.prob} Probable + + + {formatCurrency(closure.value)} + + ))} + + )} + + + {/* BOTTOM QUICK ACTIONS BAR */} Quick Actions - + navigation.navigate('LogActivity', { tab: 'call' })} > 📞 - Log Call + Call navigation.navigate('CallLogs')} + onPress={() => navigation.navigate('LogActivity', { tab: 'demo' })} > - 📜 - Call Logs + 📹 + Demo navigation.navigate('LogActivity', { tab: 'followup' })} + onPress={() => navigation.navigate('LogActivity', { tab: 'visit' })} > - 📅 - Follow-up + 🚗 + Visit + + navigation.navigate('AddOpportunity')} + > + 💼 + Deal - {/* Dashboard Grid */} + {/* Navigation Menu */} Main Activities @@ -212,10 +450,10 @@ const HomeScreen = ({ navigation }) => { onPress={() => navigation.navigate('Attendance')} /> navigation.navigate('AddOpportunity')} + onPress={() => navigation.navigate('ClientList')} /> { color={Colors.secondary} onPress={() => navigation.navigate('Incentive')} /> - navigation.navigate('LogActivity')} - /> - - - - {/* Odoo Promo/Tip Section */} - - - 💡 - - - Sales Tip - Follow up on your proposition deals to increase won rate by 20%. @@ -260,27 +481,27 @@ const styles = StyleSheet.create({ }, header: { backgroundColor: Colors.primary, - paddingBottom: 40, - paddingHorizontal: 24, + paddingBottom: 30, + paddingHorizontal: 20, flexDirection: 'row', alignItems: 'center', }, avatarContainer: { - marginRight: 15, + marginRight: 12, }, avatar: { - width: 50, - height: 50, - borderRadius: 25, + width: 44, + height: 44, + borderRadius: 22, backgroundColor: 'rgba(255,255,255,0.2)', justifyContent: 'center', alignItems: 'center', - borderWidth: 2, + borderWidth: 1.5, borderColor: 'rgba(255,255,255,0.3)', }, avatarText: { color: 'white', - fontSize: 20, + fontSize: 18, fontWeight: 'bold', }, headerTextContainer: { @@ -288,16 +509,16 @@ const styles = StyleSheet.create({ }, greeting: { color: 'rgba(255,255,255,0.7)', - fontSize: 14, + fontSize: 12, }, userName: { color: 'white', - fontSize: 22, + fontSize: 18, fontWeight: 'bold', }, bellButton: { padding: 5, - marginRight: 10, + marginRight: 8, position: 'relative', }, badge: { @@ -305,9 +526,9 @@ const styles = StyleSheet.create({ top: 0, right: 0, backgroundColor: '#ef4444', - borderRadius: 10, - minWidth: 16, - height: 16, + borderRadius: 8, + minWidth: 14, + height: 14, justifyContent: 'center', alignItems: 'center', borderWidth: 1, @@ -315,180 +536,338 @@ const styles = StyleSheet.create({ }, badgeText: { color: 'white', - fontSize: 9, + fontSize: 8, fontWeight: 'bold', - paddingHorizontal: 4, + paddingHorizontal: 3, }, settingsButton: { padding: 5, }, - settingsIcon: { - fontSize: 20, - }, profileButton: { padding: 5, - marginRight: 10, + marginRight: 8, }, - statsRow: { - flexDirection: 'row', - paddingHorizontal: 20, - marginTop: -25, - justifyContent: 'space-between', + tabSection: { + paddingTop: 16, + paddingHorizontal: 16, }, - priorityCard: { - width: (width - 60) / 3, - paddingVertical: 12, - borderRadius: 12, - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.15, - shadowRadius: 6, - elevation: 5, - }, - priorityValue: { - color: 'white', - fontSize: 18, - fontWeight: 'bold', - }, - priorityLabel: { - color: 'rgba(255,255,255,0.8)', - fontSize: 10, + sectionSubtitle: { + fontSize: 11, + color: Colors.textMuted, fontWeight: 'bold', textTransform: 'uppercase', + marginBottom: 8, + letterSpacing: 0.5, }, - focusContainer: { + horizontalTabsContainer: { + gap: 10, + paddingRight: 16, + }, + questionTile: { flexDirection: 'row', - paddingHorizontal: 20, - marginTop: 20, - justifyContent: 'space-between', - }, - scoreBox: { - backgroundColor: 'white', - width: '40%', - padding: 16, - borderRadius: 16, alignItems: 'center', - borderWidth: 1, - borderColor: Colors.border, - }, - targetBox: { backgroundColor: 'white', - width: '56%', - padding: 16, - borderRadius: 16, - justifyContent: 'center', + paddingHorizontal: 14, + paddingVertical: 10, + borderRadius: 12, borderWidth: 1, borderColor: Colors.border, + gap: 8, }, - focusLabel: { - fontSize: 9, + questionTileActive: { + backgroundColor: Colors.primary, + borderColor: Colors.primary, + }, + questionTileText: { + fontSize: 12, + fontWeight: 'bold', + color: Colors.text, + }, + questionTileTextActive: { + color: 'white', + }, + viewPort: { + padding: 16, + }, + contentContainer: { + backgroundColor: 'white', + borderRadius: 16, + padding: 16, + borderWidth: 1, + borderColor: Colors.border, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 2, + }, + contentHeader: { + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + paddingBottom: 10, + marginBottom: 12, + }, + viewPortTitle: { + fontSize: 14, + fontWeight: 'bold', + color: Colors.primary, + }, + sectionHeader: { + fontSize: 10, fontWeight: '900', color: Colors.textMuted, - letterSpacing: 1, + letterSpacing: 0.8, + marginTop: 14, marginBottom: 8, }, - scoreText: { - fontSize: 32, - fontWeight: '900', + tableCard: { + borderWidth: 1, + borderColor: Colors.borderLight, + borderRadius: 10, + backgroundColor: '#fafafa', + overflow: 'hidden', }, - tagText: { - fontSize: 8, + tableRow: { + flexDirection: 'row', + justifyContent: 'space-between', + padding: 10, + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + alignItems: 'center', + }, + rowLead: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stageDot: { + width: 8, + height: 8, + borderRadius: 4, + }, + stageName: { + fontSize: 12, fontWeight: 'bold', - color: Colors.textMuted, - marginTop: 4, + color: Colors.text, }, - progressBarBg: { - height: 6, - backgroundColor: '#f1f3f5', - borderRadius: 3, + rowRight: { + alignItems: 'flex-end', + }, + rowDetail: { + fontSize: 10, + color: Colors.textMuted, + }, + rowValue: { + fontSize: 11, + fontWeight: 'bold', + color: Colors.text, + }, + funnelItem: { + padding: 10, + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + }, + funnelName: { + fontSize: 11, + fontWeight: 'bold', + color: Colors.text, + }, + funnelStat: { + fontSize: 10, + color: Colors.textMuted, + }, + statusIndicator: { + width: 8, + height: 8, + borderRadius: 4, + }, + progressBg: { + height: 4, + backgroundColor: '#e2e8f0', + borderRadius: 2, + marginTop: 6, width: '100%', overflow: 'hidden', }, - progressBarFill: { + progressFill: { height: '100%', - backgroundColor: Colors.primary, - borderRadius: 3, + borderRadius: 2, }, - targetRow: { + weeklyRow: { flexDirection: 'row', - alignItems: 'baseline', - marginTop: 8, + justifyContent: 'space-between', + padding: 10, + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + alignItems: 'center', }, - targetValue: { - fontSize: 14, - fontWeight: 'bold', - color: Colors.text, - }, - targetGoal: { - fontSize: 10, - color: Colors.textMuted, - marginLeft: 2, - }, - noTargetText: { - fontSize: 10, - color: Colors.textMuted, - fontStyle: 'italic', - }, - statCard: { - backgroundColor: 'white', - width: (width - 56) / 2, - padding: 16, - borderRadius: 12, - borderLeftWidth: 4, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - elevation: 4, - }, - statValue: { - fontSize: 18, - fontWeight: 'bold', - color: Colors.text, - }, - statLabel: { + weekLabel: { fontSize: 12, + fontWeight: 'bold', + color: Colors.text, + }, + weekAchieved: { + fontSize: 12, + fontWeight: 'bold', + color: Colors.text, + }, + weekPct: { + fontSize: 10, + color: Colors.secondary, + fontWeight: 'bold', + }, + contributionCard: { + flexDirection: 'row', + backgroundColor: '#fafafa', + borderWidth: 1, + borderColor: Colors.borderLight, + borderRadius: 10, + padding: 12, + }, + contributionBlock: { + flex: 1, + alignItems: 'center', + }, + contribTitle: { + fontSize: 11, + fontWeight: 'bold', + color: Colors.textMuted, + }, + contribValue: { + fontSize: 15, + fontWeight: 'bold', + color: Colors.text, + marginVertical: 2, + }, + contribSub: { + fontSize: 9, + color: Colors.textMuted, + }, + contributionDivider: { + width: 1, + backgroundColor: Colors.borderLight, + marginHorizontal: 8, + }, + alertContainer: { + gap: 8, + marginBottom: 12, + }, + alertBox: { + flexDirection: 'row', + alignItems: 'center', + padding: 10, + borderRadius: 10, + borderWidth: 1, + gap: 8, + }, + alertText: { + fontSize: 11, + fontWeight: 'bold', + color: Colors.text, + }, + todoCard: { + backgroundColor: '#fafafa', + borderRadius: 10, + padding: 10, + gap: 8, + borderWidth: 1, + borderColor: Colors.borderLight, + marginBottom: 12, + }, + todoRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + todoText: { + fontSize: 12, + color: Colors.text, + fontWeight: '500', + }, + probabilityRow: { + padding: 10, + borderBottomWidth: 1, + borderBottomColor: Colors.borderLight, + }, + probType: { + fontSize: 11, + fontWeight: 'bold', + color: Colors.text, + }, + probDetails: { + fontSize: 10, color: Colors.textMuted, marginTop: 2, }, - gridContainer: { - padding: 20, - paddingTop: 10, + closureCard: { + backgroundColor: '#fafafa', + borderRadius: 12, + padding: 14, + borderWidth: 1, + borderColor: Colors.borderLight, + marginBottom: 10, + }, + closureClient: { + fontSize: 12, + fontWeight: 'bold', + color: Colors.text, + }, + probBadge: { + backgroundColor: '#ecfdf5', + paddingHorizontal: 8, + paddingVertical: 2, + borderRadius: 6, + }, + probBadgeText: { + fontSize: 9, + color: '#059669', + fontWeight: 'bold', + }, + closureValue: { + fontSize: 16, + fontWeight: '900', + color: Colors.text, + marginTop: 6, }, quickActionsContainer: { paddingHorizontal: 20, marginTop: 10, }, - quickActionsRow: { + quickActionsGrid: { flexDirection: 'row', - gap: 12, + flexWrap: 'wrap', + justifyContent: 'space-between', marginTop: 8, + gap: 8, }, quickActionBtn: { - flex: 1, + width: (width - 48) / 2, flexDirection: 'row', alignItems: 'center', - padding: 14, - borderRadius: 16, - gap: 10, + padding: 12, + borderRadius: 12, + gap: 8, borderWidth: 1, - borderColor: 'rgba(0,0,0,0.05)', + borderColor: 'rgba(0,0,0,0.03)', }, quickActionIcon: { - fontSize: 20, + fontSize: 16, }, quickActionLabel: { - fontSize: 14, - fontWeight: '900', + fontSize: 12, + fontWeight: 'bold', }, sectionTitle: { - fontSize: 14, - fontWeight: 'bold', + fontSize: 11, + fontWeight: '900', color: Colors.primary, textTransform: 'uppercase', - letterSpacing: 1, - marginBottom: 16, + letterSpacing: 0.8, + marginBottom: 12, + }, + gridContainer: { + padding: 20, + paddingTop: 10, }, grid: { flexDirection: 'row', @@ -496,65 +875,30 @@ const styles = StyleSheet.create({ justifyContent: 'space-between', }, card: { - width: (width - 64) / 2, + width: (width - 52) / 2, backgroundColor: 'white', - padding: 20, - borderRadius: 16, - marginBottom: 16, + padding: 14, + borderRadius: 12, + marginBottom: 12, alignItems: 'center', borderWidth: 1, borderColor: Colors.border, }, iconContainer: { - width: 50, - height: 50, - borderRadius: 25, - justifyContent: 'center', - alignItems: 'center', - marginBottom: 12, - }, - cardIcon: { - fontSize: 24, - }, - cardTitle: { - fontSize: 14, - fontWeight: 'bold', - color: Colors.textMuted, - }, - tipCard: { - marginHorizontal: 24, - backgroundColor: Colors.accent, - borderRadius: 15, - padding: 16, - flexDirection: 'row', - alignItems: 'center', - borderWidth: 1, - borderColor: `${Colors.secondary}20`, - }, - tipIconContainer: { width: 40, height: 40, borderRadius: 20, - backgroundColor: `${Colors.secondary}20`, justifyContent: 'center', alignItems: 'center', - marginRight: 15, + marginBottom: 8, }, - tipIcon: { - fontSize: 20, + cardIcon: { + fontSize: 18, }, - tipTextContainer: { - flex: 1, - }, - tipTitle: { - fontSize: 15, - fontWeight: 'bold', - color: Colors.secondary, - }, - tipDescription: { + cardTitle: { fontSize: 12, - color: '#4a5568', - lineHeight: 18, + fontWeight: 'bold', + color: Colors.textMuted, }, }); diff --git a/src/screens/TasksScreen.js b/src/screens/TasksScreen.js index aefdf0a..a4986ec 100644 --- a/src/screens/TasksScreen.js +++ b/src/screens/TasksScreen.js @@ -94,10 +94,15 @@ const TasksScreen = ({ navigation }) => { const [activeCategory, setActiveCategory] = useState('today'); const [inlineRemarks, setInlineRemarks] = useState({}); const [submitting, setSubmitting] = useState(null); // holds activity id being submitted + const [selectedTypeFilter, setSelectedTypeFilter] = useState(null); const fetchTasks = async () => { try { - const params = new URLSearchParams({ userId: userInfo.id, status: 'PENDING' }); + const queryObj = { status: 'PENDING' }; + if (userInfo && userInfo.role !== 'ADMIN') { + queryObj.userId = userInfo.id; + } + const params = new URLSearchParams(queryObj); const res = await api.get(`/followups?${params.toString()}`); setAllActivities(res.data); setCategorized(categorizeActivities(res.data)); @@ -143,7 +148,11 @@ const TasksScreen = ({ navigation }) => { }; const currentCat = CATEGORIES.find(c => c.key === activeCategory); - const currentItems = categorized[activeCategory] || []; + const currentCategoryTotalCount = (categorized[activeCategory] || []).length; + const currentItems = (categorized[activeCategory] || []).filter(item => { + if (!selectedTypeFilter) return true; + return item.type === selectedTypeFilter; + }); const renderItem = ({ item }) => { const type = item.type || 'FOLLOWUP'; @@ -253,7 +262,7 @@ const TasksScreen = ({ navigation }) => { styles.catBtn, isActive && { backgroundColor: 'white' }, ]} - onPress={() => setActiveCategory(cat.key)} + onPress={() => { setActiveCategory(cat.key); setSelectedTypeFilter(null); }} > {cat.label} @@ -267,6 +276,54 @@ const TasksScreen = ({ navigation }) => { + {/* Event Type Filter Row */} + + + setSelectedTypeFilter(null)} + > + + All ({currentCategoryTotalCount}) + + + {Object.entries(TYPE_ICONS).map(([type, icon]) => { + const count = (categorized[activeCategory] || []).filter(item => item.type === type).length; + if (count === 0) return null; + const isActive = selectedTypeFilter === type; + return ( + setSelectedTypeFilter(isActive ? null : type)} + > + {icon} + + {type.replace(/_/g, ' ')} + + + + {count} + + + + ); + })} + + + {/* Section Header */} @@ -387,6 +444,25 @@ const styles = StyleSheet.create({ emptyIcon: { fontSize: 52, marginBottom: 14 }, emptyTitle: { fontSize: 18, fontWeight: '900', color: '#1e293b' }, emptySub: { fontSize: 13, color: '#64748b', marginTop: 6, textAlign: 'center' }, + + // Type filters + typeFilterScroll: { paddingHorizontal: 16, marginBottom: 4 }, + typeFilterScrollContent: { gap: 8, paddingRight: 32 }, + typeFilterBtn: { + flexDirection: 'row', alignItems: 'center', gap: 6, + paddingHorizontal: 12, paddingVertical: 6, + borderRadius: 20, backgroundColor: 'white', + borderWidth: 1, borderColor: '#e2e8f0', + }, + typeFilterBtnActive: { + borderColor: Colors.primary, + backgroundColor: '#fff7ed', + }, + typeFilterIcon: { fontSize: 13 }, + typeFilterBtnText: { fontSize: 11, fontWeight: '700', color: '#64748b', textTransform: 'capitalize' }, + typeFilterBtnTextActive: { color: '#1e293b', fontWeight: '800' }, + typeFilterCount: { minWidth: 16, height: 16, borderRadius: 8, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 4 }, + typeFilterCountText: { fontSize: 9, fontWeight: '900', color: '#475569' }, }); export default TasksScreen;