# Widgets communs GEOSECTOR

## Description générale

GEOSECTOR utilise un ensemble de widgets communs réutilisables pour maintenir une cohérence visuelle et simplifier le développement. Ces widgets sont conçus pour s'adapter à la fois à l'interface web et mobile, et respectent les thèmes définis dans l'application. Ce document détaille les widgets principaux, leurs propriétés et leur utilisation.

## Interface utilisateur de base

### CustomButton

Un bouton stylisé et réutilisable avec gestion de l'état de chargement.

**Fichier**: `lib/presentation/widgets/custom_button.dart`

**Propriétés principales**:

| Propriété      | Type           | Description                                       | Requis |
|----------------|----------------|---------------------------------------------------|--------|
| onPressed      | VoidCallback?  | Fonction appelée lors du clic                      | Oui    |
| text           | String         | Texte du bouton                                   | Oui    |
| icon           | IconData?      | Icône à afficher à gauche du texte                | Non    |
| isLoading      | bool           | Afficher un indicateur de chargement              | Non    |
| width          | double?        | Largeur personnalisée                             | Non    |
| backgroundColor| Color?         | Couleur de fond (utilise primary par défaut)      | Non    |
| textColor      | Color?         | Couleur du texte (utilise white par défaut)       | Non    |

**Exemple d'utilisation**:

```dart
CustomButton(
  onPressed: () => saveData(),
  text: 'Enregistrer',
  icon: Icons.save,
  isLoading: isSaving,
  backgroundColor: Colors.green,
)
```

### CustomTextField

Champ de texte stylisé et réutilisable avec gestion des validateurs.

**Fichier**: `lib/presentation/widgets/custom_text_field.dart`

**Propriétés principales**:

| Propriété       | Type                  | Description                                     | Requis |
|-----------------|------------------------|------------------------------------------------|--------|
| controller      | TextEditingController | Contrôleur du champ                             | Oui    |
| label           | String                | Libellé du champ                                | Oui    |
| hintText        | String?               | Texte indicatif lorsque vide                    | Non    |
| prefixIcon      | IconData?             | Icône au début du champ                         | Non    |
| suffixIcon      | Widget?               | Widget à la fin du champ                        | Non    |
| obscureText     | bool                  | Masquer le texte (pour les mots de passe)       | Non    |
| keyboardType    | TextInputType         | Type de clavier                                 | Non    |
| validator       | Function?             | Fonction de validation                          | Non    |
| maxLines        | int?                  | Nombre maximum de lignes                        | Non    |
| minLines        | int?                  | Nombre minimum de lignes                        | Non    |
| readOnly        | bool                  | Lecture seule                                   | Non    |
| onChanged       | Function?             | Fonction appelée lors des modifications         | Non    |
| fillColor       | Color?                | Couleur de fond du champ                        | Non    |

**Exemple d'utilisation**:

```dart
CustomTextField(
  controller: _emailController,
  label: 'Adresse email',
  hintText: 'Saisissez votre email',
  prefixIcon: Icons.email,
  keyboardType: TextInputType.emailAddress,
  validator: (value) => value.isEmpty ? 'Email requis' : null,
)
```

## Navigation et mise en page

### DashboardAppBar

Barre d'applications personnalisée pour les tableaux de bord.

**Fichier**: `lib/presentation/widgets/dashboard_app_bar.dart`

**Propriétés principales**:

| Propriété            | Type         | Description                                      | Requis |
|----------------------|--------------|--------------------------------------------------|--------|
| title                | String       | Titre principal de l'AppBar                      | Oui    |
| pageTitle            | String?      | Titre de la page actuelle                        | Non    |
| additionalActions    | List<Widget>?| Actions supplémentaires                          | Non    |
| showNewPassageButton | bool         | Afficher le bouton "Nouveau passage"             | Non    |
| onNewPassagePressed  | VoidCallback?| Fonction appelée pour créer un nouveau passage   | Non    |
| isAdmin              | bool         | Indique si l'utilisateur est un administrateur   | Non    |

**Exemple d'utilisation**:

```dart
DashboardAppBar(
  title: 'GEOSECTOR',
  pageTitle: 'Tableau de bord',
  isAdmin: userRepository.isAdmin(),
  showNewPassageButton: true,
  onNewPassagePressed: () => Navigator.pushNamed(context, '/passage/new'),
)
```

## Visualisation cartographique

### MapboxMap

Widget de carte réutilisable utilisant Mapbox pour afficher des marqueurs et des polygones.

**Fichier**: `lib/presentation/widgets/mapbox_map.dart`

**Propriétés principales**:

| Propriété        | Type                       | Description                                  | Requis |
|------------------|----------------------------|----------------------------------------------|--------|
| initialPosition  | LatLng                     | Position initiale de la carte                | Non    |
| initialZoom      | double                     | Niveau de zoom initial                       | Non    |
| markers          | List<Marker>?              | Liste des marqueurs à afficher               | Non    |
| polygons         | List<Polygon>?             | Liste des polygones à afficher               | Non    |
| mapController    | MapController?             | Contrôleur de carte externe                  | Non    |
| onMapEvent       | Function(MapEvent)?        | Callback lors des événements de carte        | Non    |
| showControls     | bool                       | Afficher les boutons de contrôle             | Non    |
| mapStyle         | String?                    | Style de la carte Mapbox                     | Non    |

**Exemple d'utilisation**:

```dart
MapboxMap(
  initialPosition: LatLng(48.1173, -1.6778),
  initialZoom: 13.0,
  markers: sectorMarkers,
  polygons: [
    Polygon(
      points: sector.getCoordinates().map((p) => LatLng(p[0], p[1])).toList(),
      color: Color(int.parse(sector.color.replaceAll('#', '0xFF'))),
      borderStrokeWidth: 2,
      borderColor: Colors.black,
    ),
  ],
  showControls: true,
)
```

## Affichage des données

### PassagesListWidget

Widget réutilisable pour afficher une liste de passages avec filtres et actions.

**Fichier**: `lib/presentation/widgets/passages/passages_list_widget.dart`

**Propriétés principales**:

| Propriété          | Type                                    | Description                                   | Requis |
|--------------------|----------------------------------------|-----------------------------------------------|--------|
| passages           | List<Map<String, dynamic>>             | Liste des passages à afficher                 | Oui    |
| title              | String?                                | Titre de la section                           | Non    |
| maxPassages        | int?                                   | Nombre maximum de passages à afficher         | Non    |
| showFilters        | bool                                   | Afficher les filtres                          | Non    |
| showSearch         | bool                                   | Afficher la barre de recherche                | Non    |
| showActions        | bool                                   | Afficher les boutons d'action                 | Non    |
| onPassageSelected  | Function(Map<String, dynamic>)?        | Callback à la sélection d'un passage         | Non    |
| onPassageEdit      | Function(Map<String, dynamic>)?        | Callback pour éditer un passage              | Non    |
| onReceiptView      | Function(Map<String, dynamic>)?        | Callback pour voir un reçu                   | Non    |
| initialTypeFilter  | String?                                | Filtre de type initial                        | Non    |
| initialPaymentFilter| String?                               | Filtre de paiement initial                    | Non    |

**Format attendu des passages**:

```dart
[
  {
    'id': 1001,
    'address': '12 Rue des Lilas, Paris',
    'type': 1, // Correspond à AppKeys.typesPassages
    'payment': 2, // Correspond à AppKeys.typesReglements
    'date': DateTime(2025, 4, 15),
    'amount': 25.50,
    'name': 'Martin Dupont',
    'notes': 'Premier étage à droite',
    'hasError': false,
    'fkUser': 123 // ID de l'utilisateur qui a effectué le passage
  },
  // ...
]
```

**Exemple d'utilisation**:

```dart
PassagesListWidget(
  passages: passagesList,
  title: 'Historique des passages',
  showFilters: true,
  showSearch: true,
  onPassageSelected: (passage) => showPassageDetails(passage),
  onPassageEdit: (passage) => editPassage(passage),
  onReceiptView: (passage) => viewReceipt(passage),
)
```

## Visualisation de données

### ActivityChart

Graphique d'activité affichant les passages par jour/semaine/mois.

**Fichier**: `lib/presentation/widgets/charts/activity_chart.dart`

**Propriétés principales**:

| Propriété            | Type                        | Description                                   | Requis |
|----------------------|-----------------------------|-----------------------------------------------|--------|
| passageData          | List<Map<String, dynamic>>? | Données des passages                          | Non*   |
| periodType           | String                      | Type de période (Jour, Semaine, Mois)         | Non    |
| height               | double                      | Hauteur du graphique                          | Non    |
| daysToShow           | int                         | Nombre de jours à afficher                    | Non    |
| userId               | int?                        | ID de l'utilisateur pour filtrer              | Non    |
| excludePassageTypes  | List<int>                   | Types de passages à exclure                   | Non    |
| loadFromHive         | bool                        | Charger depuis Hive au lieu des données fournies | Non    |
| title                | String                      | Titre du graphique                            | Non    |
| showDataLabels       | bool                        | Afficher les étiquettes de valeur             | Non    |
| columnWidth          | double                      | Largeur des colonnes                          | Non    |
| columnSpacing        | double                      | Espacement entre les colonnes                 | Non    |
| showAllPassages      | bool                        | Afficher tous les passages sans filtrage      | Non    |

\* Soit `passageData` doit être fourni, soit `loadFromHive` doit être `true`.

**Format attendu de passageData**:

```dart
[
  {
    'date': '2025-04-01',
    'type_passage': 1,
    'nb': 5
  },
  {
    'date': '2025-04-01',
    'type_passage': 3,
    'nb': 2
  },
  // ...
]
```

**Exemple d'utilisation**:

```dart
ActivityChart(
  loadFromHive: true,
  periodType: 'Jour',
  height: 350,
  daysToShow: 15,
  userId: userRepository.userId,
  excludePassageTypes: [2, 6],
  title: 'Activité des 15 derniers jours',
)
```

## Autres widgets

### PassagePieChart et PaymentPieChart

Graphiques circulaires affichant la répartition des passages et des paiements.

**Fichiers**:
- `lib/presentation/widgets/charts/passage_pie_chart.dart`
- `lib/presentation/widgets/charts/payment_pie_chart.dart`

Ces widgets utilisent les mêmes principes que ActivityChart mais avec une visualisation circulaire.

**Exemple d'utilisation**:

```dart
Row(
  children: [
    Expanded(
      child: PassagePieChart(
        loadFromHive: true,
        title: 'Répartition par type',
        height: 300,
      ),
    ),
    Expanded(
      child: PaymentPieChart(
        loadFromHive: true,
        title: 'Répartition par règlement',
        height: 300,
      ),
    ),
  ],
)
```

## Adaptabilité et responsive design

Tous les widgets communs sont conçus pour s'adapter aux différentes tailles d'écran:

1. **Détection de la taille d'écran**:
   ```dart
   final size = MediaQuery.of(context).size;
   final isDesktop = size.width > 900;
   ```

2. **Layouts conditionnels**:
   ```dart
   isDesktop 
       ? _buildDesktopLayout() 
       : _buildMobileLayout()
   ```

3. **Flexibilité des widgets**:
   - Utilisation fréquente de `Expanded` et `Flexible`
   - Définition de contraintes minimales et maximales
   - Ajustement du nombre de colonnes selon l'espace disponible

## Bonnes pratiques implémentées

1. **Gestion des erreurs robuste** - Chaque widget inclut une gestion d'erreurs pour éviter les plantages lors des problèmes de données.

2. **Optimisation des performances** - Évitement des rebuilds inutiles et utilisation d'animations fluides.

3. **Séparation des préoccupations** - Les widgets se concentrent sur l'affichage, laissant la logique métier aux repositories.

4. **Documentation complète** - Chaque widget comporte des commentaires de documentation détaillés.

5. **Paramétrage flexible** - La plupart des widgets peuvent être personnalisés via leurs propriétés.

## Utilisation avec les thèmes

Les widgets communs respectent le thème de l'application défini dans `app_theme.dart`. Ils utilisent systématiquement:

```dart
final theme = Theme.of(context);
// Utilisation des couleurs du thème
theme.colorScheme.primary
theme.colorScheme.onPrimary
// Utilisation des styles de texte du thème
theme.textTheme.bodyLarge
```

Cela garantit une apparence cohérente et le support des thèmes clairs et sombres.
