Clean project: remove test files, debug logs, and add documentation

This commit is contained in:
dahoud
2025-10-05 13:41:33 +00:00
parent 96a17eadbd
commit 291847924c
438 changed files with 65754 additions and 32713 deletions

View File

@@ -0,0 +1,460 @@
import 'package:flutter/material.dart';
/// Widget réutilisable pour afficher un élément d'activité
///
/// Composant standardisé pour les listes d'activités récentes,
/// notifications, historiques, etc.
class ActivityItem extends StatelessWidget {
/// Titre principal de l'activité
final String title;
/// Description ou détails de l'activité
final String? description;
/// Horodatage de l'activité
final String timestamp;
/// Icône représentative de l'activité
final IconData? icon;
/// Couleur thématique de l'activité
final Color? color;
/// Type d'activité pour le style automatique
final ActivityType? type;
/// Callback lors du tap sur l'élément
final VoidCallback? onTap;
/// Style de l'élément d'activité
final ActivityItemStyle style;
/// Afficher ou non l'indicateur de statut
final bool showStatusIndicator;
const ActivityItem({
super.key,
required this.title,
this.description,
required this.timestamp,
this.icon,
this.color,
this.type,
this.onTap,
this.style = ActivityItemStyle.normal,
this.showStatusIndicator = true,
});
/// Constructeur pour une activité système
const ActivityItem.system({
super.key,
required this.title,
this.description,
required this.timestamp,
this.onTap,
}) : icon = Icons.settings,
color = const Color(0xFF6C5CE7),
type = ActivityType.system,
style = ActivityItemStyle.normal,
showStatusIndicator = true;
/// Constructeur pour une activité utilisateur
const ActivityItem.user({
super.key,
required this.title,
this.description,
required this.timestamp,
this.onTap,
}) : icon = Icons.person,
color = const Color(0xFF00B894),
type = ActivityType.user,
style = ActivityItemStyle.normal,
showStatusIndicator = true;
/// Constructeur pour une alerte
const ActivityItem.alert({
super.key,
required this.title,
this.description,
required this.timestamp,
this.onTap,
}) : icon = Icons.warning,
color = Colors.orange,
type = ActivityType.alert,
style = ActivityItemStyle.alert,
showStatusIndicator = true;
/// Constructeur pour une erreur
const ActivityItem.error({
super.key,
required this.title,
this.description,
required this.timestamp,
this.onTap,
}) : icon = Icons.error,
color = Colors.red,
type = ActivityType.error,
style = ActivityItemStyle.alert,
showStatusIndicator = true;
/// Constructeur pour une activité de succès
const ActivityItem.success({
super.key,
required this.title,
this.description,
required this.timestamp,
this.onTap,
}) : icon = Icons.check_circle,
color = const Color(0xFF00B894),
type = ActivityType.success,
style = ActivityItemStyle.normal,
showStatusIndicator = true;
@override
Widget build(BuildContext context) {
final effectiveColor = _getEffectiveColor();
final effectiveIcon = _getEffectiveIcon();
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.only(bottom: 8),
padding: _getPadding(),
decoration: _getDecoration(effectiveColor),
child: _buildContent(effectiveColor, effectiveIcon),
),
);
}
/// Contenu principal de l'élément
Widget _buildContent(Color effectiveColor, IconData effectiveIcon) {
switch (style) {
case ActivityItemStyle.minimal:
return _buildMinimalContent(effectiveColor, effectiveIcon);
case ActivityItemStyle.normal:
return _buildNormalContent(effectiveColor, effectiveIcon);
case ActivityItemStyle.detailed:
return _buildDetailedContent(effectiveColor, effectiveIcon);
case ActivityItemStyle.alert:
return _buildAlertContent(effectiveColor, effectiveIcon);
}
}
/// Contenu minimal (ligne simple)
Widget _buildMinimalContent(Color effectiveColor, IconData effectiveIcon) {
return Row(
children: [
if (showStatusIndicator)
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: effectiveColor,
shape: BoxShape.circle,
),
),
if (showStatusIndicator) const SizedBox(width: 8),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
Text(
timestamp,
style: const TextStyle(
color: Colors.grey,
fontSize: 10,
),
),
],
);
}
/// Contenu normal avec icône
Widget _buildNormalContent(Color effectiveColor, IconData effectiveIcon) {
return Row(
children: [
if (showStatusIndicator) ...[
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: effectiveColor.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
effectiveIcon,
color: effectiveColor,
size: 16,
),
),
const SizedBox(width: 12),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
if (description != null) ...[
const SizedBox(height: 2),
Text(
description!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
],
),
),
const SizedBox(width: 8),
Text(
timestamp,
style: TextStyle(
color: Colors.grey[500],
fontSize: 11,
fontWeight: FontWeight.w500,
),
),
],
);
}
/// Contenu détaillé avec plus d'informations
Widget _buildDetailedContent(Color effectiveColor, IconData effectiveIcon) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: effectiveColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
effectiveIcon,
color: effectiveColor,
size: 18,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
),
),
),
Text(
timestamp,
style: TextStyle(
color: Colors.grey[500],
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
if (description != null) ...[
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.only(left: 42),
child: Text(
description!,
style: TextStyle(
fontSize: 14,
color: Colors.grey[700],
height: 1.4,
),
),
),
],
],
);
}
/// Contenu pour les alertes avec style spécial
Widget _buildAlertContent(Color effectiveColor, IconData effectiveIcon) {
return Row(
children: [
Icon(
effectiveIcon,
color: effectiveColor,
size: 18,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: effectiveColor,
),
),
if (description != null) ...[
const SizedBox(height: 2),
Text(
description!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
],
),
),
const SizedBox(width: 8),
Text(
timestamp,
style: TextStyle(
color: Colors.grey[500],
fontSize: 11,
),
),
],
);
}
/// Couleur effective selon le type
Color _getEffectiveColor() {
if (color != null) return color!;
switch (type) {
case ActivityType.system:
return const Color(0xFF6C5CE7);
case ActivityType.user:
return const Color(0xFF00B894);
case ActivityType.organization:
return const Color(0xFF0984E3);
case ActivityType.event:
return const Color(0xFFE17055);
case ActivityType.alert:
return Colors.orange;
case ActivityType.error:
return Colors.red;
case ActivityType.success:
return const Color(0xFF00B894);
case null:
return const Color(0xFF6C5CE7);
}
}
/// Icône effective selon le type
IconData _getEffectiveIcon() {
if (icon != null) return icon!;
switch (type) {
case ActivityType.system:
return Icons.settings;
case ActivityType.user:
return Icons.person;
case ActivityType.organization:
return Icons.business;
case ActivityType.event:
return Icons.event;
case ActivityType.alert:
return Icons.warning;
case ActivityType.error:
return Icons.error;
case ActivityType.success:
return Icons.check_circle;
case null:
return Icons.circle;
}
}
/// Padding selon le style
EdgeInsets _getPadding() {
switch (style) {
case ActivityItemStyle.minimal:
return const EdgeInsets.symmetric(vertical: 4, horizontal: 8);
case ActivityItemStyle.normal:
return const EdgeInsets.all(8);
case ActivityItemStyle.detailed:
return const EdgeInsets.all(12);
case ActivityItemStyle.alert:
return const EdgeInsets.all(10);
}
}
/// Décoration selon le style
BoxDecoration _getDecoration(Color effectiveColor) {
switch (style) {
case ActivityItemStyle.minimal:
return const BoxDecoration();
case ActivityItemStyle.normal:
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.02),
blurRadius: 4,
offset: const Offset(0, 1),
),
],
);
case ActivityItemStyle.detailed:
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
);
case ActivityItemStyle.alert:
return BoxDecoration(
color: effectiveColor.withOpacity(0.05),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: effectiveColor.withOpacity(0.2),
width: 1,
),
);
}
}
}
/// Types d'activité
enum ActivityType {
system,
user,
organization,
event,
alert,
error,
success,
}
/// Styles d'élément d'activité
enum ActivityItemStyle {
minimal,
normal,
detailed,
alert,
}

View File

@@ -0,0 +1,303 @@
import 'package:flutter/material.dart';
/// Widget réutilisable pour les en-têtes de section
///
/// Composant standardisé pour tous les titres de section dans les dashboards
/// avec support pour actions, sous-titres et styles personnalisés.
class SectionHeader extends StatelessWidget {
/// Titre principal de la section
final String title;
/// Sous-titre optionnel
final String? subtitle;
/// Widget d'action à droite (bouton, icône, etc.)
final Widget? action;
/// Icône optionnelle à gauche du titre
final IconData? icon;
/// Couleur du titre et de l'icône
final Color? color;
/// Taille du titre
final double? fontSize;
/// Style de l'en-tête
final SectionHeaderStyle style;
/// Espacement en bas de l'en-tête
final double bottomSpacing;
const SectionHeader({
super.key,
required this.title,
this.subtitle,
this.action,
this.icon,
this.color,
this.fontSize,
this.style = SectionHeaderStyle.normal,
this.bottomSpacing = 12,
});
/// Constructeur pour un en-tête principal
const SectionHeader.primary({
super.key,
required this.title,
this.subtitle,
this.action,
this.icon,
}) : color = const Color(0xFF6C5CE7),
fontSize = 20,
style = SectionHeaderStyle.primary,
bottomSpacing = 16;
/// Constructeur pour un en-tête de section
const SectionHeader.section({
super.key,
required this.title,
this.subtitle,
this.action,
this.icon,
}) : color = const Color(0xFF6C5CE7),
fontSize = 16,
style = SectionHeaderStyle.normal,
bottomSpacing = 12;
/// Constructeur pour un en-tête de sous-section
const SectionHeader.subsection({
super.key,
required this.title,
this.subtitle,
this.action,
this.icon,
}) : color = const Color(0xFF374151),
fontSize = 14,
style = SectionHeaderStyle.minimal,
bottomSpacing = 8;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: bottomSpacing),
child: _buildContent(),
);
}
Widget _buildContent() {
switch (style) {
case SectionHeaderStyle.primary:
return _buildPrimaryHeader();
case SectionHeaderStyle.normal:
return _buildNormalHeader();
case SectionHeaderStyle.minimal:
return _buildMinimalHeader();
case SectionHeaderStyle.card:
return _buildCardHeader();
}
}
/// En-tête principal avec fond coloré
Widget _buildPrimaryHeader() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
color ?? const Color(0xFF6C5CE7),
(color ?? const Color(0xFF6C5CE7)).withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: (color ?? const Color(0xFF6C5CE7)).withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
if (icon != null) ...[
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
color: Colors.white,
size: 20,
),
),
const SizedBox(width: 12),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: fontSize ?? 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
if (subtitle != null) ...[
const SizedBox(height: 4),
Text(
subtitle!,
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.8),
),
),
],
],
),
),
if (action != null) action!,
],
),
);
}
/// En-tête normal avec icône et action
Widget _buildNormalHeader() {
return Row(
children: [
if (icon != null) ...[
Icon(
icon,
color: color ?? const Color(0xFF6C5CE7),
size: 20,
),
const SizedBox(width: 8),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: fontSize ?? 16,
fontWeight: FontWeight.bold,
color: color ?? const Color(0xFF6C5CE7),
),
),
if (subtitle != null) ...[
const SizedBox(height: 2),
Text(
subtitle!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
],
),
),
if (action != null) action!,
],
);
}
/// En-tête minimal simple
Widget _buildMinimalHeader() {
return Row(
children: [
if (icon != null) ...[
Icon(
icon,
color: color ?? const Color(0xFF374151),
size: 16,
),
const SizedBox(width: 6),
],
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: fontSize ?? 14,
fontWeight: FontWeight.w600,
color: color ?? const Color(0xFF374151),
),
),
),
if (action != null) action!,
],
);
}
/// En-tête avec fond de carte
Widget _buildCardHeader() {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
if (icon != null) ...[
Icon(
icon,
color: color ?? const Color(0xFF6C5CE7),
size: 20,
),
const SizedBox(width: 8),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: fontSize ?? 16,
fontWeight: FontWeight.bold,
color: color ?? const Color(0xFF6C5CE7),
),
),
if (subtitle != null) ...[
const SizedBox(height: 2),
Text(
subtitle!,
style: TextStyle(
fontSize: 12,
color: Colors.grey[600],
),
),
],
],
),
),
if (action != null) action!,
],
),
);
}
}
/// Énumération des styles d'en-tête
enum SectionHeaderStyle {
primary,
normal,
minimal,
card,
}

View File

@@ -0,0 +1,292 @@
import 'package:flutter/material.dart';
/// Widget réutilisable pour afficher une carte de statistique
///
/// Composant générique utilisé dans tous les dashboards pour afficher
/// des métriques avec icône, valeur, titre et sous-titre.
class StatCard extends StatelessWidget {
/// Titre principal de la statistique
final String title;
/// Valeur numérique ou textuelle à afficher
final String value;
/// Sous-titre ou description complémentaire
final String subtitle;
/// Icône représentative de la métrique
final IconData icon;
/// Couleur thématique de la carte
final Color color;
/// Callback optionnel lors du tap sur la carte
final VoidCallback? onTap;
/// Taille de la carte (compact, normal, large)
final StatCardSize size;
/// Style de la carte (minimal, elevated, outlined)
final StatCardStyle style;
const StatCard({
super.key,
required this.title,
required this.value,
required this.subtitle,
required this.icon,
required this.color,
this.onTap,
this.size = StatCardSize.normal,
this.style = StatCardStyle.elevated,
});
/// Constructeur pour une carte KPI simplifiée
const StatCard.kpi({
super.key,
required this.title,
required this.value,
required this.subtitle,
required this.icon,
required this.color,
this.onTap,
}) : size = StatCardSize.compact,
style = StatCardStyle.elevated;
/// Constructeur pour une carte de métrique système
const StatCard.metric({
super.key,
required this.title,
required this.value,
required this.subtitle,
required this.icon,
required this.color,
this.onTap,
}) : size = StatCardSize.normal,
style = StatCardStyle.minimal;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: _getPadding(),
decoration: _getDecoration(),
child: _buildContent(),
),
);
}
/// Contenu principal de la carte
Widget _buildContent() {
switch (size) {
case StatCardSize.compact:
return _buildCompactContent();
case StatCardSize.normal:
return _buildNormalContent();
case StatCardSize.large:
return _buildLargeContent();
}
}
/// Contenu compact pour les KPIs
Widget _buildCompactContent() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: color, size: 20),
const Spacer(),
Text(
value,
style: TextStyle(
fontWeight: FontWeight.bold,
color: color,
fontSize: 18,
),
),
],
),
const SizedBox(height: 4),
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black87,
fontSize: 12,
),
),
Text(
subtitle,
style: const TextStyle(
color: Colors.grey,
fontSize: 10,
),
),
],
);
}
/// Contenu normal pour les métriques
Widget _buildNormalContent() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: color, size: 20),
),
const Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
value,
style: TextStyle(
fontWeight: FontWeight.bold,
color: color,
fontSize: 20,
),
),
if (subtitle.isNotEmpty)
Text(
subtitle,
style: TextStyle(
color: Colors.grey[600],
fontSize: 10,
),
),
],
),
],
),
const SizedBox(height: 12),
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
fontSize: 14,
),
),
],
);
}
/// Contenu large pour les dashboards principaux
Widget _buildLargeContent() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 24),
),
const Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
value,
style: TextStyle(
fontWeight: FontWeight.bold,
color: color,
fontSize: 24,
),
),
if (subtitle.isNotEmpty)
Text(
subtitle,
style: TextStyle(
color: Colors.grey[600],
fontSize: 12,
),
),
],
),
],
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Color(0xFF1F2937),
fontSize: 16,
),
),
],
);
}
/// Padding selon la taille
EdgeInsets _getPadding() {
switch (size) {
case StatCardSize.compact:
return const EdgeInsets.all(8);
case StatCardSize.normal:
return const EdgeInsets.all(12);
case StatCardSize.large:
return const EdgeInsets.all(16);
}
}
/// Décoration selon le style
BoxDecoration _getDecoration() {
switch (style) {
case StatCardStyle.minimal:
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
);
case StatCardStyle.elevated:
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
);
case StatCardStyle.outlined:
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: color.withOpacity(0.2),
width: 1,
),
);
}
}
}
/// Énumération des tailles de carte
enum StatCardSize {
compact,
normal,
large,
}
/// Énumération des styles de carte
enum StatCardStyle {
minimal,
elevated,
outlined,
}