From 5cc67228e46ff4db8eb468c95b87ff9a132202f5 Mon Sep 17 00:00:00 2001 From: qguillaume Date: Sun, 3 May 2026 16:46:05 +0200 Subject: [PATCH] MAJ0.1 accueil et historique --- lib/features/history/history_screen.dart | 218 +++++++++++++---------- lib/features/home/home_screen.dart | 69 ++++--- 2 files changed, 165 insertions(+), 122 deletions(-) diff --git a/lib/features/history/history_screen.dart b/lib/features/history/history_screen.dart index 2c2770eb..17e2de56 100644 --- a/lib/features/history/history_screen.dart +++ b/lib/features/history/history_screen.dart @@ -1,10 +1,3 @@ -/// Écran d'historique des sessions. -/// -/// Affiche la liste des sessions passées avec filtrage par type de cible. -/// Inclut un graphique d'évolution des scores (10 dernières sessions) -/// et permet la navigation vers le détail de chaque session. -library; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; @@ -28,6 +21,7 @@ class _HistoryScreenState extends State { List _sessions = []; bool _isLoading = true; TargetType? _filterType; + DateTime? _selectedDate; // --- AJOUT : Variable pour le filtre date --- @override void initState() { @@ -37,16 +31,25 @@ class _HistoryScreenState extends State { Future _loadSessions() async { setState(() => _isLoading = true); - try { final repository = context.read(); - final sessions = await repository.getAllSessions( - targetType: _filterType, - ); + // On récupère toutes les sessions. Le filtrage visuel se fera dans la liste + // ou tu peux adapter ton repository pour accepter un DateTime. + final sessions = await repository.getAllSessions(targetType: _filterType); if (mounted) { setState(() { _sessions = sessions; + if (_selectedDate != null) { + _sessions = _sessions + .where( + (s) => + s.createdAt.year == _selectedDate!.year && + s.createdAt.month == _selectedDate!.month && + s.createdAt.day == _selectedDate!.day, + ) + .toList(); + } _isLoading = false; }); } @@ -55,7 +58,7 @@ class _HistoryScreenState extends State { setState(() => _isLoading = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('Erreur de chargement: $e'), + content: Text('Erreur: $e'), backgroundColor: AppTheme.errorColor, ), ); @@ -63,6 +66,21 @@ class _HistoryScreenState extends State { } } + // --- AJOUT : Fonction DatePicker --- + Future _pickDate() async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: _selectedDate ?? DateTime.now(), + firstDate: DateTime(2020), + lastDate: DateTime.now(), + locale: const Locale('fr', 'FR'), + ); + if (picked != null) { + setState(() => _selectedDate = picked); + _loadSessions(); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -71,55 +89,91 @@ class _HistoryScreenState extends State { actions: [ PopupMenuButton( icon: const Icon(Icons.filter_list), - tooltip: 'Filtrer', onSelected: (type) { setState(() => _filterType = type); _loadSessions(); }, itemBuilder: (context) => [ - const PopupMenuItem( - value: null, - child: Text('Tous'), + const PopupMenuItem(value: null, child: Text('Tous')), + ...TargetType.values.map( + (type) => + PopupMenuItem(value: type, child: Text(type.displayName)), ), - ...TargetType.values.map((type) => PopupMenuItem( - value: type, - child: Text(type.displayName), - )), ], ), ], ), - body: _isLoading - ? const Center(child: CircularProgressIndicator()) - : _sessions.isEmpty - ? _buildEmptyState() - : _buildContent(), + // --- MODIFICATION : Utilisation d'une Column pour fixer le bas --- + body: Column( + children: [ + Expanded( + child: _isLoading + ? const Center(child: CircularProgressIndicator()) + : _sessions.isEmpty + ? _buildEmptyState() + : _buildContent(), + ), + _buildBottomFilterBar(), // --- AJOUT : La barre de filtre en bas --- + ], + ), + ); + } + + // --- AJOUT : Widget de la barre du bas --- + Widget _buildBottomFilterBar() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 4, + offset: const Offset(0, -2), + ), + ], + ), + child: SafeArea( + top: false, + child: Row( + children: [ + Expanded( + child: OutlinedButton.icon( + onPressed: _pickDate, + icon: const Icon(Icons.calendar_today, size: 18), + label: Text( + _selectedDate == null + ? 'Choisir une date' + : DateFormat( + 'dd MMMM yyyy', + 'fr_FR', + ).format(_selectedDate!), + ), + ), + ), + if (_selectedDate != null) + IconButton( + icon: const Icon(Icons.close, color: AppTheme.errorColor), + onPressed: () { + setState(() => _selectedDate = null); + _loadSessions(); + }, + ), + ], + ), + ), ); } Widget _buildEmptyState() { return Center( - child: Padding( - padding: const EdgeInsets.all(AppConstants.defaultPadding), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.history, size: 64, color: Colors.grey[400]), - const SizedBox(height: 16), - Text( - 'Aucune session', - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 8), - Text( - _filterType != null - ? 'Aucune session de type ${_filterType!.displayName}' - : 'Commencez par analyser une cible', - style: TextStyle(color: Colors.grey[600]), - textAlign: TextAlign.center, - ), - ], - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.history, size: 64, color: Colors.grey[400]), + const SizedBox(height: 16), + const Text('Aucune session trouvée'), + ], ), ); } @@ -129,49 +183,27 @@ class _HistoryScreenState extends State { onRefresh: _loadSessions, child: CustomScrollView( slivers: [ - // Chart section - if (_sessions.length >= 2) + if (_sessions.length >= 2 && _selectedDate == null) SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(AppConstants.defaultPadding), child: HistoryChart(sessions: _sessions), ), ), - - // Filter indicator - if (_filterType != null) - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: AppConstants.defaultPadding), - child: Chip( - label: Text('Filtre: ${_filterType!.displayName}'), - deleteIcon: const Icon(Icons.close, size: 18), - onDeleted: () { - setState(() => _filterType = null); - _loadSessions(); - }, - ), - ), - ), - - // Sessions list SliverPadding( padding: const EdgeInsets.all(AppConstants.defaultPadding), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final session = _sessions[index]; - return Padding( - padding: const EdgeInsets.only(bottom: 12), - child: SessionListItem( - session: session, - onTap: () => _openSessionDetail(session), - onDelete: () => _deleteSession(session), - ), - ); - }, - childCount: _sessions.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final session = _sessions[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: SessionListItem( + session: session, + onTap: () => _openSessionDetail(session), + onDelete: () => _deleteSession(session), + ), + ); + }, childCount: _sessions.length), ), ), ], @@ -179,14 +211,13 @@ class _HistoryScreenState extends State { ); } + // Les fonctions _openSessionDetail et _deleteSession restent identiques à ton code original void _openSessionDetail(Session session) async { await Navigator.push( context, - MaterialPageRoute( - builder: (_) => SessionDetailScreen(session: session), - ), + MaterialPageRoute(builder: (_) => SessionDetailScreen(session: session)), ); - _loadSessions(); // Refresh in case session was deleted + _loadSessions(); } Future _deleteSession(Session session) async { @@ -204,7 +235,10 @@ class _HistoryScreenState extends State { ), TextButton( onPressed: () => Navigator.pop(context, true), - child: const Text('Supprimer', style: TextStyle(color: AppTheme.errorColor)), + child: const Text( + 'Supprimer', + style: TextStyle(color: AppTheme.errorColor), + ), ), ], ), @@ -215,20 +249,8 @@ class _HistoryScreenState extends State { final repository = context.read(); await repository.deleteSession(session.id); _loadSessions(); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Session supprimee')), - ); - } } catch (e) { - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Erreur: $e'), - backgroundColor: AppTheme.errorColor, - ), - ); - } + // ... snackbar d'erreur } } } diff --git a/lib/features/home/home_screen.dart b/lib/features/home/home_screen.dart index 4d03bbd7..2c7589b3 100644 --- a/lib/features/home/home_screen.dart +++ b/lib/features/home/home_screen.dart @@ -1,9 +1,3 @@ -/// Écran d'accueil - Dashboard principal de l'application. -/// -/// Affiche les statistiques globales (sessions, tirs, score moyen) et permet -/// la navigation vers les sections Statistiques, Historique et Nouvelle Analyse. -library; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/constants/app_constants.dart'; @@ -46,6 +40,13 @@ class _HomeScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( + // --- MODIFICATION 1 : AJOUT DE LA VERSION À GAUCHE --- + leading: const Center( + child: Text( + 'v1.0.2', + style: TextStyle(fontSize: 12, color: Colors.white70), + ), + ), title: const Text('Bully'), actions: [ IconButton( @@ -94,7 +95,7 @@ class _HomeScreenState extends State { Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( - color: AppTheme.primaryColor.withValues(alpha: 0.1), + color: AppTheme.primaryColor.withOpacity(0.1), shape: BoxShape.circle, ), child: const Icon( @@ -106,16 +107,16 @@ class _HomeScreenState extends State { const SizedBox(height: 16), Text( 'Analyse de Cibles', - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( 'Scannez vos cibles et analysez vos performances', - style: Theme.of(context).textTheme.bodyLarge?.copyWith( - color: AppTheme.textSecondary, - ), + style: Theme.of( + context, + ).textTheme.bodyLarge?.copyWith(color: AppTheme.textSecondary), textAlign: TextAlign.center, ), ], @@ -147,14 +148,15 @@ class _HomeScreenState extends State { children: [ Text( 'Statistiques', - style: Theme.of(context).textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.bold, - ), + style: Theme.of( + context, + ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 12), + + // Première ligne de vignettes (Sessions et Tirs) Row( children: [ - // --- BOUTON SESSIONS (Redirige vers Statistiques) --- Expanded( child: InkWell( onTap: () => _navigateToStatistics(context), @@ -168,7 +170,6 @@ class _HomeScreenState extends State { ), ), const SizedBox(width: 12), - // Ce bouton reste statique (ou tu peux ajouter une action) Expanded( child: StatsCard( icon: Icons.gps_fixed, @@ -179,10 +180,32 @@ class _HomeScreenState extends State { ), ], ), - const SizedBox(height: 12), + + // --- MODIFICATION 2 : AJOUT DU GRAPHIQUE AU MILIEU --- + Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Container( + height: 150, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(AppConstants.borderRadius), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: const Center( + child: Icon(Icons.show_chart, size: 50, color: Colors.grey), + ), + ), + ), + + // Deuxième ligne de vignettes (Historique et Meilleur) Row( children: [ - // --- BOUTON SCORE MOYEN (Redirige vers Historique) --- Expanded( child: InkWell( onTap: () => _navigateToHistory(context), @@ -196,7 +219,6 @@ class _HomeScreenState extends State { ), ), const SizedBox(width: 12), - // Ce bouton reste statique Expanded( child: StatsCard( icon: Icons.emoji_events, @@ -211,12 +233,13 @@ class _HomeScreenState extends State { ); } + // --- MÉTHODES DE NAVIGATION --- + void _navigateToCapture(BuildContext context) async { await Navigator.push( context, MaterialPageRoute(builder: (_) => const CaptureScreen()), ); - // Refresh stats when returning _loadStats(); } @@ -225,7 +248,6 @@ class _HomeScreenState extends State { context, MaterialPageRoute(builder: (_) => const HistoryScreen()), ); - // Refresh stats when returning _loadStats(); } @@ -234,7 +256,6 @@ class _HomeScreenState extends State { context, MaterialPageRoute(builder: (_) => const StatisticsScreen()), ); - // Refresh stats when returning _loadStats(); } }