feat(session): ajout du sélecteur de date et persistance dans l'historique de tir
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:io';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import '../models/session.dart';
|
import '../models/session.dart';
|
||||||
@@ -123,10 +124,10 @@ class DatabaseHelper {
|
|||||||
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
|
||||||
if (oldVersion < 2) {
|
if (oldVersion < 2) {
|
||||||
// Migration from single-target sessions to multi-target sessions
|
// Migration from single-target sessions to multi-target sessions
|
||||||
|
|
||||||
// 1. Rename existing sessions table to a temporary name
|
// 1. Rename existing sessions table to a temporary name
|
||||||
await db.execute('ALTER TABLE ${AppConstants.sessionsTable} RENAME TO sessions_old');
|
await db.execute('ALTER TABLE ${AppConstants.sessionsTable} RENAME TO sessions_old');
|
||||||
|
|
||||||
// 2. Create the new sessions table
|
// 2. Create the new sessions table
|
||||||
await db.execute('''
|
await db.execute('''
|
||||||
CREATE TABLE ${AppConstants.sessionsTable} (
|
CREATE TABLE ${AppConstants.sessionsTable} (
|
||||||
@@ -220,7 +221,7 @@ class DatabaseHelper {
|
|||||||
// 6. Cleanup
|
// 6. Cleanup
|
||||||
await db.execute('DROP TABLE sessions_old');
|
await db.execute('DROP TABLE sessions_old');
|
||||||
await db.execute('DROP TABLE shots_old');
|
await db.execute('DROP TABLE shots_old');
|
||||||
|
|
||||||
// 7. Re-create indexes
|
// 7. Re-create indexes
|
||||||
await db.execute('''
|
await db.execute('''
|
||||||
CREATE INDEX idx_target_analyses_session_id ON ${AppConstants.targetAnalysesTable}(session_id)
|
CREATE INDEX idx_target_analyses_session_id ON ${AppConstants.targetAnalysesTable}(session_id)
|
||||||
@@ -285,9 +286,14 @@ class DatabaseHelper {
|
|||||||
Future<int> insertSession(Session session) async {
|
Future<int> insertSession(Session session) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
return await db.transaction((txn) async {
|
return await db.transaction((txn) async {
|
||||||
|
// INTERCEPTION ET FIX DE LA DATE ICI :
|
||||||
|
// On extrait la map générée et on force la colonne created_at avec la vraie date de l'objet
|
||||||
|
final Map<String, dynamic> sessionMap = Map.from(session.toMap());
|
||||||
|
sessionMap['created_at'] = session.createdAt.toIso8601String();
|
||||||
|
|
||||||
await txn.insert(
|
await txn.insert(
|
||||||
AppConstants.sessionsTable,
|
AppConstants.sessionsTable,
|
||||||
session.toMap(),
|
sessionMap, // On utilise notre map corrigée au lieu de session.toMap() direct !
|
||||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -447,7 +453,7 @@ class DatabaseHelper {
|
|||||||
|
|
||||||
Future<int> updateWeapon(Weapon weapon) async {
|
Future<int> updateWeapon(Weapon weapon) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
// Start a transaction to ensure both updates succeed
|
// Start a transaction to ensure both updates succeed
|
||||||
return await db.transaction((txn) async {
|
return await db.transaction((txn) async {
|
||||||
// 1. Update weapon in armory
|
// 1. Update weapon in armory
|
||||||
@@ -472,7 +478,7 @@ class DatabaseHelper {
|
|||||||
|
|
||||||
Future<int> deleteWeapon(String id) async {
|
Future<int> deleteWeapon(String id) async {
|
||||||
final db = await database;
|
final db = await database;
|
||||||
|
|
||||||
return await db.transaction((txn) async {
|
return await db.transaction((txn) async {
|
||||||
// 1. Unlink sessions from this weapon but KEEP the name (gravé à jamais)
|
// 1. Unlink sessions from this weapon but KEEP the name (gravé à jamais)
|
||||||
await txn.update(
|
await txn.update(
|
||||||
@@ -538,4 +544,4 @@ class DatabaseHelper {
|
|||||||
await db.close();
|
await db.close();
|
||||||
_database = null;
|
_database = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ class SessionRepository {
|
|||||||
required List<TargetAnalysis> analyses,
|
required List<TargetAnalysis> analyses,
|
||||||
String? notes,
|
String? notes,
|
||||||
int distance = 25,
|
int distance = 25,
|
||||||
|
DateTime? date,
|
||||||
}) async {
|
}) async {
|
||||||
final session = Session(
|
final session = Session(
|
||||||
id: id ?? _uuid.v4(),
|
id: id ?? _uuid.v4(),
|
||||||
@@ -34,7 +35,7 @@ class SessionRepository {
|
|||||||
weaponId: weaponId,
|
weaponId: weaponId,
|
||||||
maxShotsPerTarget: maxShotsPerTarget,
|
maxShotsPerTarget: maxShotsPerTarget,
|
||||||
analyses: analyses,
|
analyses: analyses,
|
||||||
createdAt: DateTime.now(),
|
createdAt: date ?? DateTime.now(),
|
||||||
notes: notes,
|
notes: notes,
|
||||||
distance: distance,
|
distance: distance,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
x: impact.x,
|
x: impact.x,
|
||||||
y: impact.y,
|
y: impact.y,
|
||||||
score: impact.suggestedScore,
|
score: impact.suggestedScore,
|
||||||
analysisId: '', // Will be set when saving
|
analysisId: '',
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
@@ -537,8 +537,7 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
_imagePath = correctedPath;
|
_imagePath = correctedPath;
|
||||||
_correctedImagePath = correctedPath;
|
_correctedImagePath = correctedPath;
|
||||||
_distortionCorrectionEnabled = true;
|
_distortionCorrectionEnabled = true;
|
||||||
_imageAspectRatio =
|
_imageAspectRatio = 1.0;
|
||||||
1.0; // The corrected image is always square (side x side)
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,18 +615,18 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
// 3. Application immédiate de la transformation (méthode asynchrone)
|
// 3. Application immédiate de la transformation (méthode asynchrone)
|
||||||
await applyDistortionCorrection();
|
await applyDistortionCorrection();
|
||||||
} else {
|
} else {
|
||||||
notifyListeners(); // On prévient quand même si pas de correction
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> runFullDistortionWorkflow() async {
|
Future<void> runFullDistortionWorkflow() async {
|
||||||
_state = AnalysisState.loading; // Affiche un spinner sur votre UI
|
_state = AnalysisState.loading;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
calculateDistortion(); // Calcule les paramètres
|
calculateDistortion();
|
||||||
await applyDistortionCorrection(); // Génère le fichier corrigé
|
await applyDistortionCorrection();
|
||||||
_distortionCorrectionEnabled = true; // Active l'affichage
|
_distortionCorrectionEnabled = true;
|
||||||
_state = AnalysisState.success;
|
_state = AnalysisState.success;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_errorMessage = "Erreur de rendu : $e";
|
_errorMessage = "Erreur de rendu : $e";
|
||||||
@@ -688,12 +687,7 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifiant d'analyse temporaire
|
final service = AiExportService();
|
||||||
final analysisId = _shots.isNotEmpty && _shots.first.analysisId.isNotEmpty
|
|
||||||
? _shots.first.analysisId
|
|
||||||
: 'analysis_${DateTime.now().millisecondsSinceEpoch}';
|
|
||||||
|
|
||||||
final service = AiExportService(); // Local instanciation for simplicity
|
|
||||||
|
|
||||||
_state = AnalysisState.loading;
|
_state = AnalysisState.loading;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -716,13 +710,14 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save the session (legacy standalone flow)
|
/// Save the session (Refactorisé pour appliquer le paramètre de date personnalisé)
|
||||||
Future<TargetAnalysis> saveSession({
|
Future<TargetAnalysis> saveSession({
|
||||||
String? notes,
|
String? notes,
|
||||||
String? sessionId,
|
String? sessionId,
|
||||||
String? weaponName,
|
String? weaponName,
|
||||||
String? weaponId,
|
String? weaponId,
|
||||||
int? distance,
|
int? distance,
|
||||||
|
DateTime? date, // <-- REÇOIT PROPREMENT LA DATE DE DEBUT DE SESSION
|
||||||
}) async {
|
}) async {
|
||||||
if (_imagePath == null || _targetType == null) {
|
if (_imagePath == null || _targetType == null) {
|
||||||
throw Exception('Cannot save: missing image or target type');
|
throw Exception('Cannot save: missing image or target type');
|
||||||
@@ -748,6 +743,7 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
if (existingSession != null) {
|
if (existingSession != null) {
|
||||||
await _sessionRepository.addAnalysisToSession(sessionId, targetAnalysis);
|
await _sessionRepository.addAnalysisToSession(sessionId, targetAnalysis);
|
||||||
} else {
|
} else {
|
||||||
|
// CORRECTION : Utilise la date injectée plutôt que DateTime.now()
|
||||||
await _sessionRepository.createSession(
|
await _sessionRepository.createSession(
|
||||||
id: sessionId,
|
id: sessionId,
|
||||||
weapon: weaponName ?? 'Inconnue',
|
weapon: weaponName ?? 'Inconnue',
|
||||||
@@ -756,9 +752,11 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
analyses: [targetAnalysis],
|
analyses: [targetAnalysis],
|
||||||
notes: notes,
|
notes: notes,
|
||||||
distance: distance ?? 25,
|
distance: distance ?? 25,
|
||||||
|
date: date ?? DateTime.now(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// CORRECTION : S'applique aussi au flux alternatif sans ID préalable
|
||||||
await _sessionRepository.createSession(
|
await _sessionRepository.createSession(
|
||||||
weapon: weaponName ?? 'Inconnue',
|
weapon: weaponName ?? 'Inconnue',
|
||||||
weaponId: weaponId,
|
weaponId: weaponId,
|
||||||
@@ -766,6 +764,7 @@ class AnalysisProvider extends ChangeNotifier {
|
|||||||
analyses: [targetAnalysis],
|
analyses: [targetAnalysis],
|
||||||
notes: notes,
|
notes: notes,
|
||||||
distance: distance ?? 25,
|
distance: distance ?? 25,
|
||||||
|
date: date ?? DateTime.now(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -568,7 +568,6 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSaveSessionDialog(BuildContext context, AnalysisProvider provider) {
|
void _showSaveSessionDialog(BuildContext context, AnalysisProvider provider) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -614,6 +613,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
|||||||
weaponName: sessionProvider.currentWeapon,
|
weaponName: sessionProvider.currentWeapon,
|
||||||
weaponId: sessionProvider.currentWeaponId,
|
weaponId: sessionProvider.currentWeaponId,
|
||||||
distance: sessionProvider.distance,
|
distance: sessionProvider.distance,
|
||||||
|
date: sessionProvider.sessionDate,
|
||||||
);
|
);
|
||||||
|
|
||||||
sessionProvider.addAnalysis(analysis);
|
sessionProvider.addAnalysis(analysis);
|
||||||
@@ -645,6 +645,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
|||||||
weaponName: sessionProvider.currentWeapon,
|
weaponName: sessionProvider.currentWeapon,
|
||||||
weaponId: sessionProvider.currentWeaponId,
|
weaponId: sessionProvider.currentWeaponId,
|
||||||
distance: sessionProvider.distance,
|
distance: sessionProvider.distance,
|
||||||
|
date: sessionProvider.sessionDate, // <-- CORRECTION : Prise en compte de la date ici aussi !
|
||||||
);
|
);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
@@ -666,4 +667,4 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import '../../data/models/target_analysis.dart';
|
|||||||
import '../../data/models/weapon.dart';
|
import '../../data/models/weapon.dart';
|
||||||
|
|
||||||
class SessionProvider extends ChangeNotifier {
|
class SessionProvider extends ChangeNotifier {
|
||||||
|
DateTime? _sessionDate;
|
||||||
|
DateTime? get sessionDate => _sessionDate;
|
||||||
String? _currentWeaponName;
|
String? _currentWeaponName;
|
||||||
String? _currentWeaponId;
|
String? _currentWeaponId;
|
||||||
int _shotsPerTarget = 5;
|
int _shotsPerTarget = 5;
|
||||||
@@ -23,11 +25,12 @@ class SessionProvider extends ChangeNotifier {
|
|||||||
int get totalSessionScore => _currentAnalyses.fold(0, (sum, a) => sum + a.totalScore);
|
int get totalSessionScore => _currentAnalyses.fold(0, (sum, a) => sum + a.totalScore);
|
||||||
int get targetCount => _currentAnalyses.length;
|
int get targetCount => _currentAnalyses.length;
|
||||||
|
|
||||||
void startSession(String weaponName, int shots, String sessionId, {String? weaponId, int distance = 25}) {
|
void startSession(String weaponName, int shots, String sessionId, {String? weaponId, int distance = 25, DateTime? date}) {
|
||||||
_currentWeaponName = weaponName;
|
_currentWeaponName = weaponName;
|
||||||
_currentWeaponId = weaponId;
|
_currentWeaponId = weaponId;
|
||||||
_shotsPerTarget = shots;
|
_shotsPerTarget = shots;
|
||||||
_distance = distance;
|
_distance = distance;
|
||||||
|
_sessionDate = date ?? DateTime.now();
|
||||||
_activeSessionId = sessionId;
|
_activeSessionId = sessionId;
|
||||||
_currentAnalyses.clear();
|
_currentAnalyses.clear();
|
||||||
_isSessionActive = true;
|
_isSessionActive = true;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:intl/intl.dart'; // Utile pour formater proprement la date en français
|
||||||
import '../../core/constants/app_constants.dart';
|
import '../../core/constants/app_constants.dart';
|
||||||
import '../../core/theme/app_theme.dart';
|
import '../../core/theme/app_theme.dart';
|
||||||
import '../../data/models/weapon.dart';
|
import '../../data/models/weapon.dart';
|
||||||
@@ -18,13 +19,15 @@ class SessionSetupScreen extends StatefulWidget {
|
|||||||
class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
// Plus besoin du _weaponController, on force la sélection
|
|
||||||
int _shotsPerTarget = 5;
|
int _shotsPerTarget = 5;
|
||||||
int _distance = 25;
|
int _distance = 25;
|
||||||
List<Weapon> _availableWeapons = [];
|
List<Weapon> _availableWeapons = [];
|
||||||
Weapon? _selectedWeapon;
|
Weapon? _selectedWeapon;
|
||||||
bool _isLoadingWeapons = true;
|
bool _isLoadingWeapons = true;
|
||||||
|
|
||||||
|
// AJOUT DE LA VARIABLE DATE : Initialisée par défaut à maintenant
|
||||||
|
DateTime _selectedDate = DateTime.now();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -39,7 +42,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_availableWeapons = weapons;
|
_availableWeapons = weapons;
|
||||||
// Si la liste n'est pas vide et qu'aucune arme n'est sélectionnée, on sélectionne la première par défaut
|
|
||||||
if (_availableWeapons.isNotEmpty && _selectedWeapon == null) {
|
if (_availableWeapons.isNotEmpty && _selectedWeapon == null) {
|
||||||
_selectedWeapon = _availableWeapons.first;
|
_selectedWeapon = _availableWeapons.first;
|
||||||
_updateSettingsForWeapon(_selectedWeapon!);
|
_updateSettingsForWeapon(_selectedWeapon!);
|
||||||
@@ -49,25 +51,47 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Petite méthode pour mettre à jour les curseurs selon l'arme sélectionnée
|
|
||||||
void _updateSettingsForWeapon(Weapon weapon) {
|
void _updateSettingsForWeapon(Weapon weapon) {
|
||||||
_shotsPerTarget = weapon.magazineCapacity;
|
_shotsPerTarget = weapon.magazineCapacity;
|
||||||
_distance = (weapon.type == WeaponType.handgun) ? 25 : 50;
|
_distance = (weapon.type == WeaponType.handgun) ? 25 : 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fonction pour ouvrir le calendrier si l'utilisateur clique sur le champ
|
||||||
|
Future<void> _pickDate() async {
|
||||||
|
final DateTime? picked = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: _selectedDate,
|
||||||
|
firstDate: DateTime(2020),
|
||||||
|
lastDate: DateTime(2100),
|
||||||
|
locale: const Locale('fr', 'FR'), // Force le calendrier en français
|
||||||
|
);
|
||||||
|
if (picked != null && picked != _selectedDate) {
|
||||||
|
setState(() {
|
||||||
|
// On garde aussi l'heure actuelle lors du changement de date
|
||||||
|
final now = DateTime.now();
|
||||||
|
_selectedDate = DateTime(picked.year, picked.month, picked.day, now.hour, now.minute);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _startSession() {
|
void _startSession() {
|
||||||
// Sécurité supplémentaire : impossible de démarrer sans arme
|
|
||||||
if (_formKey.currentState!.validate() && _selectedWeapon != null) {
|
if (_formKey.currentState!.validate() && _selectedWeapon != null) {
|
||||||
final repository = context.read<SessionRepository>();
|
final repository = context.read<SessionRepository>();
|
||||||
final sessionProvider = context.read<SessionProvider>();
|
final sessionProvider = context.read<SessionProvider>();
|
||||||
|
|
||||||
final sessionId = repository.generateId();
|
final sessionId = repository.generateId();
|
||||||
|
|
||||||
|
// PASSE-PARTOUT : On envoie la session au provider.
|
||||||
|
// Note : Si ton `sessionProvider.startSession` ne prend pas encore la date en paramètre,
|
||||||
|
// pas de panique, la compilation passera car Dart tolère les arguments nommés optionnels
|
||||||
|
// s'ils sont déjà présents, ou tu pourras l'ajouter à ta méthode startSession.
|
||||||
sessionProvider.startSession(
|
sessionProvider.startSession(
|
||||||
_selectedWeapon!.displayName,
|
_selectedWeapon!.displayName,
|
||||||
_shotsPerTarget,
|
_shotsPerTarget,
|
||||||
sessionId,
|
sessionId,
|
||||||
weaponId: _selectedWeapon!.id,
|
weaponId: _selectedWeapon!.id,
|
||||||
distance: _distance,
|
distance: _distance,
|
||||||
|
date: _selectedDate,
|
||||||
);
|
);
|
||||||
|
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
@@ -79,6 +103,9 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Petit formatage sympa en français (ex: "27 mai 2026")
|
||||||
|
final String formattedDate = DateFormat('dd MMMM yyyy', 'fr_FR').format(_selectedDate);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Configuration de la session'),
|
title: const Text('Configuration de la session'),
|
||||||
@@ -93,7 +120,7 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Choix de l\'arme',
|
'Informations générales',
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -128,7 +155,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (_) => const WeaponListScreen()),
|
MaterialPageRoute(builder: (_) => const WeaponListScreen()),
|
||||||
).then((_) {
|
).then((_) {
|
||||||
// Quand on revient de l'armurerie, on recharge la liste !
|
|
||||||
_loadWeapons();
|
_loadWeapons();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -139,28 +165,50 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
DropdownButtonFormField<Weapon>(
|
Column(
|
||||||
value: _selectedWeapon,
|
children: [
|
||||||
decoration: InputDecoration(
|
DropdownButtonFormField<Weapon>(
|
||||||
labelText: 'Sélectionner une arme',
|
value: _selectedWeapon,
|
||||||
prefixIcon: const Icon(Icons.shield),
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(
|
labelText: 'Sélectionner une arme',
|
||||||
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
prefixIcon: const Icon(Icons.shield),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
items: _availableWeapons.map((w) => DropdownMenuItem(
|
||||||
|
value: w,
|
||||||
|
child: Text(w.displayName),
|
||||||
|
)).toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
setState(() {
|
||||||
|
_selectedWeapon = value;
|
||||||
|
_updateSettingsForWeapon(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
// On a retiré le choix "null / Autre", l'utilisateur DOIT choisir une arme existante
|
|
||||||
items: _availableWeapons.map((w) => DropdownMenuItem(
|
// NOUVEAU CHAMP : Sélecteur de date interactif
|
||||||
value: w,
|
InkWell(
|
||||||
child: Text(w.displayName),
|
onTap: _availableWeapons.isEmpty ? null : _pickDate,
|
||||||
)).toList(),
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
onChanged: (value) {
|
child: IgnorePointer(
|
||||||
if (value != null) {
|
child: TextFormField(
|
||||||
setState(() {
|
decoration: InputDecoration(
|
||||||
_selectedWeapon = value;
|
labelText: 'Date de la session',
|
||||||
_updateSettingsForWeapon(value);
|
prefixIcon: const Icon(Icons.calendar_today),
|
||||||
});
|
border: OutlineInputBorder(
|
||||||
}
|
borderRadius: BorderRadius.circular(AppConstants.borderRadius),
|
||||||
},
|
),
|
||||||
|
),
|
||||||
|
controller: TextEditingController(text: formattedDate),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
@@ -252,7 +300,6 @@ class _SessionSetupScreenState extends State<SessionSetupScreen> {
|
|||||||
const SizedBox(height: 48),
|
const SizedBox(height: 48),
|
||||||
|
|
||||||
ElevatedButton.icon(
|
ElevatedButton.icon(
|
||||||
// On désactive le bouton si la liste est vide ou si aucune arme n'est sélectionnée
|
|
||||||
onPressed: (_availableWeapons.isEmpty || _selectedWeapon == null)
|
onPressed: (_availableWeapons.isEmpty || _selectedWeapon == null)
|
||||||
? null
|
? null
|
||||||
: _startSession,
|
: _startSession,
|
||||||
|
|||||||
Reference in New Issue
Block a user