219 lines
6.6 KiB
Dart
219 lines
6.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:geosector_app/core/data/models/passage_model.dart';
|
|
import 'package:geosector_app/core/data/models/sector_model.dart';
|
|
import 'package:geosector_app/core/constants/app_keys.dart';
|
|
import 'package:geosector_app/shared/app_theme.dart';
|
|
|
|
class SectorDistributionCard extends StatefulWidget {
|
|
final String title;
|
|
final double? height;
|
|
final EdgeInsetsGeometry? padding;
|
|
final bool forceRefresh;
|
|
|
|
const SectorDistributionCard({
|
|
Key? key,
|
|
this.title = 'Répartition par secteur',
|
|
this.height,
|
|
this.padding,
|
|
this.forceRefresh = false,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
State<SectorDistributionCard> createState() => _SectorDistributionCardState();
|
|
}
|
|
|
|
class _SectorDistributionCardState extends State<SectorDistributionCard> {
|
|
List<Map<String, dynamic>> sectorStats = [];
|
|
bool isLoading = true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadSectorData();
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(SectorDistributionCard oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
|
|
// Recharger les données si forceRefresh est passé à true
|
|
if (widget.forceRefresh && !oldWidget.forceRefresh) {
|
|
_loadSectorData();
|
|
}
|
|
}
|
|
|
|
Future<void> _loadSectorData() async {
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
// S'assurer que les boîtes Hive sont ouvertes
|
|
if (!Hive.isBoxOpen(AppKeys.sectorsBoxName)) {
|
|
await Hive.openBox<SectorModel>(AppKeys.sectorsBoxName);
|
|
}
|
|
|
|
if (!Hive.isBoxOpen(AppKeys.passagesBoxName)) {
|
|
await Hive.openBox<PassageModel>(AppKeys.passagesBoxName);
|
|
}
|
|
|
|
// Récupérer tous les secteurs
|
|
final sectorsBox = Hive.box<SectorModel>(AppKeys.sectorsBoxName);
|
|
final List<SectorModel> sectors = sectorsBox.values.toList();
|
|
|
|
// Récupérer tous les passages
|
|
final passagesBox = Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
|
final List<PassageModel> passages = passagesBox.values.toList();
|
|
|
|
// Compter les passages par secteur (en excluant ceux où fkType==2)
|
|
final Map<int, int> sectorCounts = {};
|
|
|
|
for (final passage in passages) {
|
|
// Exclure les passages où fkType==2
|
|
if (passage.fkSector != null && passage.fkType != 2) {
|
|
sectorCounts[passage.fkSector!] =
|
|
(sectorCounts[passage.fkSector!] ?? 0) + 1;
|
|
}
|
|
}
|
|
|
|
// Préparer les données pour l'affichage
|
|
List<Map<String, dynamic>> stats = [];
|
|
for (final sector in sectors) {
|
|
final count = sectorCounts[sector.id] ?? 0;
|
|
if (count > 0) {
|
|
stats.add({
|
|
'name': sector.libelle,
|
|
'count': count,
|
|
'color': sector.color.isEmpty
|
|
? 0xFF4B77BE
|
|
: int.tryParse(sector.color.replaceAll('#', '0xFF')) ??
|
|
0xFF4B77BE,
|
|
});
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
sectorStats = stats;
|
|
isLoading = false;
|
|
});
|
|
} catch (e) {
|
|
debugPrint('Erreur lors du chargement des données de secteur: $e');
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
height: widget.height,
|
|
padding: widget.padding ?? const EdgeInsets.all(AppTheme.spacingM),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
|
|
boxShadow: AppTheme.cardShadow,
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
widget.title,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
if (isLoading)
|
|
const SizedBox(
|
|
width: 16,
|
|
height: 16,
|
|
child: CircularProgressIndicator(strokeWidth: 2),
|
|
)
|
|
else
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh, size: 20),
|
|
onPressed: _loadSectorData,
|
|
tooltip: 'Rafraîchir',
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppTheme.spacingM),
|
|
Expanded(
|
|
child: isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: sectorStats.isEmpty
|
|
? const Center(
|
|
child: Text('Aucune donnée de secteur disponible'))
|
|
: ListView.builder(
|
|
itemCount: sectorStats.length,
|
|
itemBuilder: (context, index) {
|
|
final sector = sectorStats[index];
|
|
return _buildSectorItem(
|
|
context,
|
|
sector['name'],
|
|
sector['count'],
|
|
Color(sector['color']),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSectorItem(
|
|
BuildContext context,
|
|
String name,
|
|
int count,
|
|
Color color,
|
|
) {
|
|
final totalCount =
|
|
sectorStats.fold(0, (sum, item) => sum + (item['count'] as int));
|
|
final percentage = totalCount > 0 ? (count / totalCount) * 100 : 0;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: AppTheme.spacingS),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
name,
|
|
style: const TextStyle(fontSize: 14),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Text(
|
|
'$count (${percentage.toInt()}%)',
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
LinearProgressIndicator(
|
|
value: percentage / 100,
|
|
backgroundColor: Colors.grey[200],
|
|
valueColor: AlwaysStoppedAnimation<Color>(color),
|
|
minHeight: 8,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|