igcrmmobile/src/screens/HomeScreen.js

562 lines
19 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

import React, { useContext, useEffect, useState, useCallback } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ScrollView, StatusBar, Dimensions, Alert } from 'react-native';
import { AuthContext } from '../context/AuthContext';
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';
const { width } = Dimensions.get('window');
const HomeScreen = ({ navigation }) => {
const { userInfo, logout } = useContext(AuthContext);
const insets = useSafeAreaInsets();
const [stats, setStats] = useState({
pipelineCount: 0,
monthlyRevenue: 0,
performance: null,
target: null,
overdueCount: 0,
todayCount: 0,
newCount: 0
});
const [unreadCount, setUnreadCount] = useState(0);
const fetchStats = async () => {
try {
const response = await api.get('/dashboard/stats');
if (response.data) {
setStats({
pipelineCount: response.data.kpis.pipelineCount,
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
});
}
const notifRes = await api.get('/notifications/unread-count');
setUnreadCount(notifRes.data.count);
if (notifRes.data.count > 0) {
const latestNotifs = await api.get('/notifications/my');
const performanceAlert = latestNotifs.data.find(n => n.type === 'PERFORMANCE_ALERT' && !n.isRead);
if (performanceAlert) {
Alert.alert(
"Performance Alert ⚠️",
performanceAlert.body,
[{ text: "Check My Performance", onPress: () => navigation.navigate('MyTarget') }]
);
}
}
} catch (error) {
console.error('Error fetching dashboard stats:', error);
}
};
useFocusEffect(
useCallback(() => {
fetchStats();
}, [])
);
const StatCard = ({ title, value, color }) => (
<View style={[styles.statCard, { borderLeftColor: color }]}>
<Text style={styles.statValue}>{value}</Text>
<Text style={styles.statLabel}>{title}</Text>
</View>
);
const MenuCard = ({ title, icon, color, onPress }) => (
<TouchableOpacity
style={styles.card}
onPress={onPress}
activeOpacity={0.8}
>
<View style={[styles.iconContainer, { backgroundColor: color + '15' }]}>
<Text style={styles.cardIcon}>{icon}</Text>
</View>
<Text style={styles.cardTitle}>{title}</Text>
</TouchableOpacity>
);
const formatCurrency = (value) => {
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}k`;
}
return `${value}`;
};
return (
<View style={styles.container}>
<StatusBar backgroundColor={Colors.primary} barStyle="light-content" />
<ScrollView bounces={false} contentContainerStyle={{ paddingBottom: 40 }}>
{/* Header Section */}
<View style={[styles.header, { paddingTop: insets.top + 20 }]}>
<View style={styles.avatarContainer}>
<View style={styles.avatar}>
<Text style={styles.avatarText}>{userInfo?.name?.charAt(0) || 'U'}</Text>
</View>
</View>
<View style={styles.headerTextContainer}>
<Text style={styles.greeting}>Good morning,</Text>
<Text style={styles.userName}>{userInfo?.name || 'User'}</Text>
</View>
<TouchableOpacity onPress={() => navigation.navigate('MyTarget')} style={styles.bellButton}>
<Bell size={24} color="white" />
{unreadCount > 0 && (
<View style={styles.badge}>
<Text style={styles.badgeText}>{unreadCount}</Text>
</View>
)}
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('ChangePassword')} style={styles.profileButton}>
<User size={20} color="white" />
</TouchableOpacity>
<TouchableOpacity onPress={logout} style={styles.settingsButton}>
<LogOut size={20} color="white" />
</TouchableOpacity>
</View>
{/* Smart Priority Cards */}
<View style={styles.statsRow}>
<View style={[styles.priorityCard, { backgroundColor: '#FF6B6B' }]}>
<Text style={styles.priorityValue}>{stats.overdueCount}</Text>
<Text style={styles.priorityLabel}>Overdue</Text>
</View>
<View style={[styles.priorityCard, { backgroundColor: '#4DABF7' }]}>
<Text style={styles.priorityValue}>{stats.todayCount}</Text>
<Text style={styles.priorityLabel}>Today</Text>
</View>
<View style={[styles.priorityCard, { backgroundColor: '#51CF66' }]}>
<Text style={styles.priorityValue}>{stats.newCount}</Text>
<Text style={styles.priorityLabel}>New</Text>
</View>
</View>
{/* Score & Target Section */}
<View style={styles.focusContainer}>
<View style={styles.scoreBox}>
<Text style={styles.focusLabel}>PERFORMANCE SCORE</Text>
<Text style={[styles.scoreText, { color: stats.performance?.score > 80 ? '#27ae60' : stats.performance?.score > 50 ? '#f39c12' : '#e74c3c' }]}>
{stats.performance ? Math.round(stats.performance.score) : '--'}
</Text>
<Text style={styles.tagText}>{stats.performance?.tag.replace('_', ' ') || 'NO DATA'}</Text>
</View>
<TouchableOpacity
style={styles.targetBox}
onPress={() => navigation.navigate('MyTarget')}
activeOpacity={0.8}
>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
<Text style={[styles.focusLabel, { marginBottom: 0 }]}>MONTHLY TARGET</Text>
<Text style={{ fontSize: 10 }}></Text>
</View>
{stats.target ? (
<>
<View style={styles.progressBarBg}>
<View style={[styles.progressBarFill, { width: `${Math.min(100, (stats.target.achieved / stats.target.monthly) * 100)}%` }]} />
</View>
<View style={styles.targetRow}>
<Text style={styles.targetValue}>{(stats.target.achieved / 1000).toFixed(1)}k</Text>
<Text style={styles.targetGoal}>/ ₹{(stats.target.monthly / 1000).toFixed(0)}k</Text>
</View>
</>
) : (
<Text style={styles.noTargetText}>No target assigned</Text>
)}
</TouchableOpacity>
</View>
{/* Quick Actions */}
<View style={styles.quickActionsContainer}>
<Text style={styles.sectionTitle}>Quick Actions</Text>
<View style={styles.quickActionsRow}>
<TouchableOpacity
style={[styles.quickActionBtn, { backgroundColor: '#eef2ff' }]}
onPress={() => navigation.navigate('LogActivity', { tab: 'call' })}
>
<Text style={styles.quickActionIcon}>📞</Text>
<Text style={[styles.quickActionLabel, { color: Colors.primary }]}>Log Call</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.quickActionBtn, { backgroundColor: '#fdf4ff' }]}
onPress={() => navigation.navigate('CallLogs')}
>
<Text style={styles.quickActionIcon}>📜</Text>
<Text style={[styles.quickActionLabel, { color: '#c026d3' }]}>Call Logs</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.quickActionBtn, { backgroundColor: '#f0fdf4' }]}
onPress={() => navigation.navigate('LogActivity', { tab: 'followup' })}
>
<Text style={styles.quickActionIcon}>📅</Text>
<Text style={[styles.quickActionLabel, { color: '#16a34a' }]}>Follow-up</Text>
</TouchableOpacity>
</View>
</View>
{/* Dashboard Grid */}
<View style={styles.gridContainer}>
<Text style={styles.sectionTitle}>Main Activities</Text>
<View style={styles.grid}>
<MenuCard
title="Attendance"
icon="📅"
color={Colors.primary}
onPress={() => navigation.navigate('Attendance')}
/>
<MenuCard
title="Enquiries"
icon="📝"
color={Colors.primary}
onPress={() => navigation.navigate('EnquiryList')}
/>
<MenuCard
title="Expenses"
icon="💸"
color={Colors.primary}
onPress={() => navigation.navigate('Expense')}
/>
<MenuCard
title="Incentives"
icon="🏆"
color={Colors.secondary}
onPress={() => navigation.navigate('Incentive')}
/>
<MenuCard
title="Marketing"
icon="📢"
color={Colors.primary}
onPress={() => navigation.navigate('LogActivity')}
/>
</View>
</View>
{/* Odoo Promo/Tip Section */}
<View style={styles.tipCard}>
<View style={styles.tipIconContainer}>
<Text style={styles.tipIcon}>💡</Text>
</View>
<View style={styles.tipTextContainer}>
<Text style={styles.tipTitle}>Sales Tip</Text>
<Text style={styles.tipDescription}>Follow up on your proposition deals to increase won rate by 20%.</Text>
</View>
</View>
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
header: {
backgroundColor: Colors.primary,
paddingBottom: 40,
paddingHorizontal: 24,
flexDirection: 'row',
alignItems: 'center',
},
avatarContainer: {
marginRight: 15,
},
avatar: {
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: 'rgba(255,255,255,0.2)',
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: 'rgba(255,255,255,0.3)',
},
avatarText: {
color: 'white',
fontSize: 20,
fontWeight: 'bold',
},
headerTextContainer: {
flex: 1,
},
greeting: {
color: 'rgba(255,255,255,0.7)',
fontSize: 14,
},
userName: {
color: 'white',
fontSize: 22,
fontWeight: 'bold',
},
bellButton: {
padding: 5,
marginRight: 10,
position: 'relative',
},
badge: {
position: 'absolute',
top: 0,
right: 0,
backgroundColor: '#ef4444',
borderRadius: 10,
minWidth: 16,
height: 16,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: Colors.primary,
},
badgeText: {
color: 'white',
fontSize: 9,
fontWeight: 'bold',
paddingHorizontal: 4,
},
settingsButton: {
padding: 5,
},
settingsIcon: {
fontSize: 20,
},
profileButton: {
padding: 5,
marginRight: 10,
},
statsRow: {
flexDirection: 'row',
paddingHorizontal: 20,
marginTop: -25,
justifyContent: 'space-between',
},
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,
fontWeight: 'bold',
textTransform: 'uppercase',
},
focusContainer: {
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',
borderWidth: 1,
borderColor: Colors.border,
},
focusLabel: {
fontSize: 9,
fontWeight: '900',
color: Colors.textMuted,
letterSpacing: 1,
marginBottom: 8,
},
scoreText: {
fontSize: 32,
fontWeight: '900',
},
tagText: {
fontSize: 8,
fontWeight: 'bold',
color: Colors.textMuted,
marginTop: 4,
},
progressBarBg: {
height: 6,
backgroundColor: '#f1f3f5',
borderRadius: 3,
width: '100%',
overflow: 'hidden',
},
progressBarFill: {
height: '100%',
backgroundColor: Colors.primary,
borderRadius: 3,
},
targetRow: {
flexDirection: 'row',
alignItems: 'baseline',
marginTop: 8,
},
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: {
fontSize: 12,
color: Colors.textMuted,
marginTop: 2,
},
gridContainer: {
padding: 20,
paddingTop: 10,
},
quickActionsContainer: {
paddingHorizontal: 20,
marginTop: 10,
},
quickActionsRow: {
flexDirection: 'row',
gap: 12,
marginTop: 8,
},
quickActionBtn: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
padding: 14,
borderRadius: 16,
gap: 10,
borderWidth: 1,
borderColor: 'rgba(0,0,0,0.05)',
},
quickActionIcon: {
fontSize: 20,
},
quickActionLabel: {
fontSize: 14,
fontWeight: '900',
},
sectionTitle: {
fontSize: 14,
fontWeight: 'bold',
color: Colors.primary,
textTransform: 'uppercase',
letterSpacing: 1,
marginBottom: 16,
},
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
card: {
width: (width - 64) / 2,
backgroundColor: 'white',
padding: 20,
borderRadius: 16,
marginBottom: 16,
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,
},
tipIcon: {
fontSize: 20,
},
tipTextContainer: {
flex: 1,
},
tipTitle: {
fontSize: 15,
fontWeight: 'bold',
color: Colors.secondary,
},
tipDescription: {
fontSize: 12,
color: '#4a5568',
lineHeight: 18,
},
});
export default HomeScreen;