bfd35905c9
- Added favicon and icon assets for the mobile app. - Implemented EmotionPicker component for selecting emotions with child options. - Created hooks for fetching emotions, feeds, and rapport data. - Established API service for handling requests and authentication. - Integrated Zustand for state management of user authentication. - Defined color palette for the application. - Set up TypeScript types for various entities including User, Emotion, and Feed. - Configured Babel and TypeScript for the mobile project. - Added package.json with necessary dependencies for Expo and React Native.
122 lines
5.7 KiB
TypeScript
122 lines
5.7 KiB
TypeScript
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
|
import { useRouter } from 'expo-router';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useAuthStore } from '@/lib/auth-store';
|
|
import { useRapport } from '@/hooks/useRapport';
|
|
import { useSaisies } from '@/hooks/useTracker';
|
|
import { Colors } from '@/lib/colors';
|
|
|
|
export default function DashboardScreen() {
|
|
const router = useRouter();
|
|
const { user } = useAuthStore();
|
|
const { data: rapport } = useRapport('month');
|
|
const { data: saisies } = useSaisies();
|
|
|
|
const recentSaisies = (saisies || []).slice(0, 3);
|
|
|
|
return (
|
|
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
|
<Text style={styles.greeting}>Bonjour, {user?.prenom} 👋</Text>
|
|
<Text style={styles.subtitle}>Comment allez-vous aujourd'hui ?</Text>
|
|
|
|
{/* Actions rapides */}
|
|
<View style={styles.actionsRow}>
|
|
<TouchableOpacity style={styles.actionCard} onPress={() => router.push('/(tabs)/journal/new')}>
|
|
<View style={[styles.actionIcon, { backgroundColor: `${Colors.primary}30` }]}>
|
|
<Ionicons name="add-circle" size={28} color={Colors.primary} />
|
|
</View>
|
|
<Text style={styles.actionText}>Nouvelle saisie</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.actionCard} onPress={() => router.push('/(tabs)/journal/reports')}>
|
|
<View style={[styles.actionIcon, { backgroundColor: `${Colors.secondary}30` }]}>
|
|
<Ionicons name="bar-chart" size={28} color={Colors.secondary} />
|
|
</View>
|
|
<Text style={styles.actionText}>Mes rapports</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Stats du mois */}
|
|
{rapport && (
|
|
<View style={styles.statsContainer}>
|
|
<Text style={styles.sectionTitle}>Ce mois-ci</Text>
|
|
<View style={styles.statsRow}>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statValue}>{rapport.stats.total_saisies}</Text>
|
|
<Text style={styles.statLabel}>Saisies</Text>
|
|
</View>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statValue}>{rapport.stats.intensite_moyenne?.toFixed(1) || '—'}</Text>
|
|
<Text style={styles.statLabel}>Intensite moy.</Text>
|
|
</View>
|
|
<View style={styles.statCard}>
|
|
<Text style={styles.statValue}>{rapport.stats.emotion_dominante?.icone || '—'}</Text>
|
|
<Text style={styles.statLabel}>{rapport.stats.emotion_dominante?.nom || 'Aucune'}</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)}
|
|
|
|
{/* Dernieres saisies */}
|
|
{recentSaisies.length > 0 && (
|
|
<View style={styles.recentContainer}>
|
|
<View style={styles.sectionHeader}>
|
|
<Text style={styles.sectionTitle}>Dernieres emotions</Text>
|
|
<TouchableOpacity onPress={() => router.push('/(tabs)/journal')}>
|
|
<Text style={styles.seeAll}>Tout voir</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{recentSaisies.map((saisie) => (
|
|
<View key={saisie.id} style={styles.saisieItem}>
|
|
<View style={[styles.emotionIcon, { backgroundColor: `${saisie.emotion?.couleur}20` }]}>
|
|
<Text style={styles.emotionEmoji}>{saisie.emotion?.icone || '🔵'}</Text>
|
|
</View>
|
|
<View style={styles.saisieContent}>
|
|
<Text style={styles.saisieEmotion}>{saisie.emotion?.nom}</Text>
|
|
<Text style={styles.saisieDate}>
|
|
{new Date(saisie.date_saisie).toLocaleDateString('fr-FR')} — Intensite: {saisie.intensite}/10
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))}
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: Colors.white },
|
|
content: { padding: 20 },
|
|
greeting: { fontSize: 28, fontWeight: '800', color: Colors.black, marginTop: 8 },
|
|
subtitle: { fontSize: 16, color: Colors.gray[500], marginTop: 4, marginBottom: 24 },
|
|
actionsRow: { flexDirection: 'row', gap: 12, marginBottom: 24 },
|
|
actionCard: {
|
|
flex: 1, backgroundColor: Colors.gray[50], borderRadius: 16, padding: 20,
|
|
alignItems: 'center', gap: 12, borderWidth: 1, borderColor: Colors.gray[100],
|
|
},
|
|
actionIcon: { width: 56, height: 56, borderRadius: 16, justifyContent: 'center', alignItems: 'center' },
|
|
actionText: { fontSize: 14, fontWeight: '600', color: Colors.gray[700] },
|
|
statsContainer: { marginBottom: 24 },
|
|
sectionTitle: { fontSize: 18, fontWeight: '700', color: Colors.black, marginBottom: 12 },
|
|
statsRow: { flexDirection: 'row', gap: 12 },
|
|
statCard: {
|
|
flex: 1, backgroundColor: Colors.gray[50], borderRadius: 16, padding: 16,
|
|
alignItems: 'center', borderWidth: 1, borderColor: Colors.gray[100],
|
|
},
|
|
statValue: { fontSize: 24, fontWeight: '800', color: Colors.black },
|
|
statLabel: { fontSize: 12, color: Colors.gray[500], marginTop: 4 },
|
|
recentContainer: { marginBottom: 24 },
|
|
sectionHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 },
|
|
seeAll: { fontSize: 14, fontWeight: '600', color: Colors.secondary },
|
|
saisieItem: {
|
|
flexDirection: 'row', alignItems: 'center', gap: 12, padding: 14,
|
|
backgroundColor: Colors.gray[50], borderRadius: 14, marginBottom: 8,
|
|
borderWidth: 1, borderColor: Colors.gray[100],
|
|
},
|
|
emotionIcon: { width: 44, height: 44, borderRadius: 12, justifyContent: 'center', alignItems: 'center' },
|
|
emotionEmoji: { fontSize: 22 },
|
|
saisieContent: { flex: 1 },
|
|
saisieEmotion: { fontSize: 15, fontWeight: '600', color: Colors.black },
|
|
saisieDate: { fontSize: 13, color: Colors.gray[500], marginTop: 2 },
|
|
});
|