🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
277 lines
10 KiB
Dart
277 lines
10 KiB
Dart
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:geosector_app/core/data/models/sector_model.dart';
|
|
import 'package:geosector_app/core/services/current_user_service.dart';
|
|
import 'package:geosector_app/presentation/widgets/sector_distribution_card.dart';
|
|
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
|
|
import 'package:geosector_app/presentation/widgets/members_board_passages.dart';
|
|
import 'package:geosector_app/core/constants/app_keys.dart';
|
|
import 'package:geosector_app/core/theme/app_theme.dart';
|
|
import 'package:geosector_app/presentation/widgets/app_scaffold.dart';
|
|
|
|
/// Widget de contenu du tableau de bord unifié (sans scaffold)
|
|
class HomeContent extends StatefulWidget {
|
|
const HomeContent({super.key});
|
|
|
|
@override
|
|
State<HomeContent> createState() => _HomeContentState();
|
|
}
|
|
|
|
class _HomeContentState extends State<HomeContent> {
|
|
// Détection du rôle
|
|
late final bool isAdmin;
|
|
late final int currentUserId;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// Déterminer le rôle de l'utilisateur et le mode d'affichage
|
|
final currentUser = userRepository.getCurrentUser();
|
|
isAdmin = CurrentUserService.instance.shouldShowAdminUI;
|
|
currentUserId = currentUser?.id ?? 0;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
debugPrint('Building HomeContent (isAdmin: $isAdmin)');
|
|
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final isDesktop = screenWidth > 800;
|
|
|
|
// Récupérer l'opération en cours
|
|
final currentOperation = userRepository.getCurrentOperation();
|
|
|
|
// Titre dynamique avec l'ID et le nom de l'opération
|
|
final String title = currentOperation != null
|
|
? 'Opération #${currentOperation.id} ${currentOperation.name}'
|
|
: 'Opération';
|
|
|
|
// Retourner seulement le contenu (sans scaffold)
|
|
return SingleChildScrollView(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: isDesktop ? AppTheme.spacingL : AppTheme.spacingS,
|
|
vertical: AppTheme.spacingL,
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Titre
|
|
Text(
|
|
title,
|
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: AppTheme.spacingM),
|
|
|
|
// LIGNE 1 : Graphiques de répartition (type de passage et mode de paiement)
|
|
isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: _buildPassageTypeCard(context),
|
|
),
|
|
const SizedBox(width: AppTheme.spacingM),
|
|
Expanded(
|
|
child: _buildPaymentTypeCard(context),
|
|
),
|
|
],
|
|
)
|
|
: Column(
|
|
children: [
|
|
_buildPassageTypeCard(context),
|
|
const SizedBox(height: AppTheme.spacingM),
|
|
_buildPaymentTypeCard(context),
|
|
],
|
|
),
|
|
|
|
const SizedBox(height: AppTheme.spacingL),
|
|
|
|
// Tableau détaillé des membres - uniquement pour admin sur Web
|
|
if (isAdmin && kIsWeb) ...[
|
|
const MembersBoardPassages(),
|
|
const SizedBox(height: AppTheme.spacingL),
|
|
],
|
|
|
|
// LIGNE 2 : Carte de répartition par secteur (uniquement si > 1 secteur)
|
|
// Le widget filtre automatiquement selon le rôle de l'utilisateur
|
|
ValueListenableBuilder<Box<SectorModel>>(
|
|
valueListenable: Hive.box<SectorModel>(AppKeys.sectorsBoxName).listenable(),
|
|
builder: (context, Box<SectorModel> box, child) {
|
|
// Filtrer les secteurs pour les users
|
|
int sectorCount;
|
|
if (isAdmin) {
|
|
sectorCount = box.values.length;
|
|
} else {
|
|
final userSectors = userRepository.getUserSectors();
|
|
sectorCount = userSectors.length;
|
|
}
|
|
|
|
// N'afficher que s'il y a plus d'un secteur
|
|
if (sectorCount <= 1) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
return SectorDistributionCard(
|
|
title: '$sectorCount secteurs',
|
|
);
|
|
},
|
|
),
|
|
|
|
const SizedBox(height: AppTheme.spacingL),
|
|
|
|
// LIGNE 3 : Graphique d'activité
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
|
|
boxShadow: AppTheme.cardShadow,
|
|
),
|
|
child: ActivityChart(
|
|
height: 350,
|
|
showAllPassages: isAdmin, // Admin voit tout, user voit tous les passages de ses secteurs
|
|
title: isAdmin ? 'Passages' : 'Mes passages',
|
|
daysToShow: 7,
|
|
showPeriodButtons: true,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: AppTheme.spacingL),
|
|
|
|
// Actions rapides - uniquement pour admin sur le web
|
|
if (isAdmin && kIsWeb) ...[
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
|
|
boxShadow: AppTheme.cardShadow,
|
|
),
|
|
padding: const EdgeInsets.all(AppTheme.spacingM),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'Actions sur cette opération',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 16,
|
|
color: AppTheme.primaryColor,
|
|
),
|
|
),
|
|
const SizedBox(height: AppTheme.spacingM),
|
|
Wrap(
|
|
spacing: AppTheme.spacingM,
|
|
runSpacing: AppTheme.spacingM,
|
|
children: [
|
|
_buildActionButton(
|
|
context,
|
|
'Exporter les données',
|
|
Icons.file_download_outlined,
|
|
AppTheme.primaryColor,
|
|
() {},
|
|
),
|
|
_buildActionButton(
|
|
context,
|
|
'Gérer les secteurs',
|
|
Icons.map_outlined,
|
|
AppTheme.accentColor,
|
|
() {},
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// Construit la carte de répartition par type de passage
|
|
Widget _buildPassageTypeCard(BuildContext context) {
|
|
return PassageSummaryCard(
|
|
title: isAdmin ? 'Passages' : 'Passages de mes secteurs',
|
|
titleColor: AppTheme.primaryColor,
|
|
titleIcon: Icons.route,
|
|
height: 300,
|
|
useValueListenable: true,
|
|
showAllPassages: isAdmin, // Admin voit tout, user voit tous les passages de ses secteurs
|
|
userId: null, // Pas de filtre par userId, on filtre par secteurs assignés
|
|
excludePassageTypes: const [], // Afficher tous les types de passages
|
|
customTotalDisplay: (total) => '$total passage${total > 1 ? 's' : ''}',
|
|
isDesktop: MediaQuery.of(context).size.width > 800,
|
|
backgroundIcon: Icons.route,
|
|
backgroundIconColor: AppTheme.primaryColor,
|
|
backgroundIconOpacity: 0.07,
|
|
backgroundIconSize: 180,
|
|
);
|
|
}
|
|
|
|
// Construit la carte de répartition par mode de paiement
|
|
Widget _buildPaymentTypeCard(BuildContext context) {
|
|
return PaymentSummaryCard(
|
|
title: isAdmin ? 'Règlements' : 'Mes règlements',
|
|
titleColor: AppTheme.buttonSuccessColor,
|
|
titleIcon: Icons.euro,
|
|
height: 300,
|
|
useValueListenable: true,
|
|
showAllPayments: isAdmin, // Admin voit tout, user voit uniquement ses règlements (fkUser)
|
|
userId: null, // Le filtre fkUser est géré automatiquement dans PaymentSummaryCard
|
|
customTotalDisplay: (total) => '${total.toStringAsFixed(2)} €',
|
|
isDesktop: MediaQuery.of(context).size.width > 800,
|
|
backgroundIcon: Icons.euro,
|
|
backgroundIconColor: AppTheme.primaryColor,
|
|
backgroundIconOpacity: 0.07,
|
|
backgroundIconSize: 180,
|
|
);
|
|
}
|
|
|
|
Widget _buildActionButton(
|
|
BuildContext context,
|
|
String label,
|
|
IconData icon,
|
|
Color color,
|
|
VoidCallback onPressed,
|
|
) {
|
|
return ElevatedButton.icon(
|
|
onPressed: onPressed,
|
|
icon: Icon(icon),
|
|
label: Text(label),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: color,
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: AppTheme.spacingL,
|
|
vertical: AppTheme.spacingM,
|
|
),
|
|
elevation: 2,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Page autonome du tableau de bord unifié utilisant AppScaffold
|
|
class HomePage extends StatelessWidget {
|
|
const HomePage({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// Utiliser le mode d'affichage pour déterminer l'UI
|
|
final isAdmin = CurrentUserService.instance.shouldShowAdminUI;
|
|
|
|
return AppScaffold(
|
|
key: ValueKey('home_scaffold_${isAdmin ? 'admin' : 'user'}'),
|
|
selectedIndex: 0, // Dashboard/Home est toujours l'index 0
|
|
pageTitle: 'Tableau de bord',
|
|
body: const HomeContent(),
|
|
);
|
|
}
|
|
} |