MAJ0.1 accueil et historique

This commit is contained in:
qguillaume
2026-05-03 16:46:05 +02:00
parent 105eb1cab0
commit 5cc67228e4
2 changed files with 165 additions and 122 deletions

View File

@@ -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<HistoryScreen> {
List<Session> _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<HistoryScreen> {
Future<void> _loadSessions() async {
setState(() => _isLoading = true);
try {
final repository = context.read<SessionRepository>();
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<HistoryScreen> {
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<HistoryScreen> {
}
}
// --- AJOUT : Fonction DatePicker ---
Future<void> _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<HistoryScreen> {
actions: [
PopupMenuButton<TargetType?>(
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<HistoryScreen> {
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<HistoryScreen> {
);
}
// 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<void> _deleteSession(Session session) async {
@@ -204,7 +235,10 @@ class _HistoryScreenState extends State<HistoryScreen> {
),
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<HistoryScreen> {
final repository = context.read<SessionRepository>();
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
}
}
}