# Guide de Contribution - UnionFlow Mobile Merci de votre intérêt pour contribuer à UnionFlow Mobile! Ce guide vous aidera à démarrer. --- ## Table des Matières 1. [Code of Conduct](#code-of-conduct) 2. [Comment Contribuer](#comment-contribuer) 3. [Setup Environnement](#setup-environnement) 4. [Standards de Code](#standards-de-code) 5. [Architecture](#architecture) 6. [Workflow Git](#workflow-git) 7. [Tests](#tests) 8. [Pull Requests](#pull-requests) 9. [Code Review](#code-review) 10. [Bonnes Pratiques](#bonnes-pratiques) --- ## Code of Conduct En contribuant à ce projet, vous acceptez de respecter notre Code de Conduite: - 🤝 Respecter tous les contributeurs - 💬 Communication constructive et professionnelle - 🎯 Focus sur le projet et ses objectifs - 🚫 Zéro tolérance pour le harcèlement ou discrimination --- ## Comment Contribuer ### Types de Contributions ✅ **Bug Fixes** - Correction de bugs ✅ **Features** - Nouvelles fonctionnalités ✅ **Documentation** - Amélioration de la doc ✅ **Tests** - Ajout ou amélioration des tests ✅ **Performance** - Optimisations ✅ **Refactoring** - Amélioration du code existant ### Avant de Commencer 1. **Vérifier les issues existantes** - Peut-être que quelqu'un travaille déjà dessus 2. **Créer une issue** - Si le bug/feature n'existe pas encore 3. **Discuter** - Commenter l'issue pour proposer votre approche 4. **Assignation** - Se faire assigner l'issue avant de commencer --- ## Setup Environnement ### Prérequis | Outil | Version Minimale | |-------|------------------| | Flutter | 3.5.3+ | | Dart | 3.5.3+ | | Git | Latest | | Android Studio | Latest (pour Android) | | Xcode | 14+ (pour iOS, macOS uniquement) | ### Installation ```bash # 1. Cloner le repo git clone https://git.lions.dev/lionsdev/unionflow-mobile-apps.git cd unionflow-mobile-apps # 2. Installer les dépendances flutter pub get # 3. Générer le code (build_runner) flutter pub run build_runner build --delete-conflicting-outputs # 4. Vérifier que tout fonctionne flutter doctor flutter analyze flutter test ``` ### Configuration Backend Local Pour développer en local, vous avez besoin du backend Quarkus: ```bash cd ../unionflow-server-impl-quarkus mvn clean quarkus:dev ``` Backend disponible sur: `http://localhost:8085` --- ## Standards de Code ### Linting Nous utilisons **flutter_lints** avec configuration stricte. ```bash # Analyser le code flutter analyze # Formater le code flutter format lib/ ``` ### Conventions de Nommage **Fichiers**: snake_case ``` ✅ dashboard_bloc.dart ❌ DashboardBloc.dart ❌ dashboard-bloc.dart ``` **Classes**: PascalCase ```dart ✅ class DashboardBloc {} ❌ class dashboardBloc {} ❌ class Dashboard_Bloc {} ``` **Variables/Fonctions**: camelCase ```dart ✅ final userName = 'John'; ✅ void getUserData() {} ❌ final user_name = 'John'; ❌ void get_user_data() {} ``` **Constantes**: lowerCamelCase (pas SCREAMING_CASE en Dart) ```dart ✅ static const baseUrl = 'https://api.lions.dev'; ❌ static const BASE_URL = 'https://api.lions.dev'; ``` **Private members**: préfixe `_` ```dart ✅ String _privateVariable; ✅ void _privateMethod() {} ``` ### Documentation **Classes publiques**: ```dart /// Service pour gérer l'authentification Keycloak. /// /// Ce service gère le login, logout et refresh token /// avec stockage sécurisé des credentials. class KeycloakAuthService { // ... } ``` **Méthodes publiques**: ```dart /// Authentifie un utilisateur avec username et password. /// /// Retourne [User] si succès, `null` si échec. /// Stocke les tokens dans [FlutterSecureStorage]. Future login(String username, String password) async { // ... } ``` **TODO Comments**: ```dart // TODO(username): Ajouter support biométrique // FIXME(username): Bug lors du logout après timeout // HACK(username): Workaround temporaire, à refactoriser ``` --- ## Architecture UnionFlow Mobile suit **Clean Architecture + BLoC**. ### Structure d'une Feature ``` features/ └── my_feature/ ├── data/ │ ├── datasources/ │ │ └── my_feature_remote_datasource.dart │ ├── models/ │ │ ├── my_model.dart │ │ └── my_model.g.dart │ └── repositories/ │ └── my_feature_repository_impl.dart ├── domain/ │ ├── entities/ │ │ └── my_entity.dart │ ├── repositories/ │ │ └── my_feature_repository.dart │ └── usecases/ │ └── get_my_data.dart └── presentation/ ├── bloc/ │ ├── my_feature_bloc.dart │ ├── my_feature_event.dart │ └── my_feature_state.dart ├── pages/ │ └── my_feature_page.dart └── widgets/ └── my_feature_card.dart ``` ### Règles de Dépendance ✅ **Presentation** peut dépendre de **Domain** ✅ **Data** peut dépendre de **Domain** ❌ **Domain** ne doit JAMAIS dépendre de **Data** ou **Presentation** ### Checklist d'une Nouvelle Feature - [ ] Créer les **Entities** dans `domain/entities/` - [ ] Créer le **Repository Interface** dans `domain/repositories/` - [ ] Créer les **Use Cases** dans `domain/usecases/` - [ ] Créer les **Models** dans `data/models/` avec `@JsonSerializable` - [ ] Créer le **Data Source** dans `data/datasources/` - [ ] Implémenter le **Repository** dans `data/repositories/` - [ ] Créer **Events** dans `presentation/bloc/` - [ ] Créer **States** dans `presentation/bloc/` - [ ] Créer le **BLoC** dans `presentation/bloc/` - [ ] Créer les **Pages** dans `presentation/pages/` - [ ] Créer les **Widgets** dans `presentation/widgets/` - [ ] Enregistrer dans **DI** (`core/di/injection_container.dart`) - [ ] Ajouter **Tests** - [ ] Mettre à jour **Documentation** --- ## Workflow Git ### Branches **Branches principales**: - `main` - Production (protégée) - `develop` - Développement (protégée) **Branches de feature**: ```bash feature/- ``` Exemples: ``` feature/123-add-biometric-login feature/456-fix-dashboard-crash feature/789-improve-cache-performance ``` ### Workflow ```bash # 1. Créer une branche depuis develop git checkout develop git pull origin develop git checkout -b feature/123-add-biometric-login # 2. Développer et commiter git add . git commit -m "feat: add biometric authentication" # 3. Pousser la branche git push origin feature/123-add-biometric-login # 4. Créer une Pull Request vers develop ``` ### Commits Nous suivons **Conventional Commits**: **Format**: ``` (): [optional body] [optional footer] ``` **Types**: - `feat` - Nouvelle fonctionnalité - `fix` - Correction de bug - `docs` - Documentation - `style` - Formatage (pas de changement de code) - `refactor` - Refactoring (ni bug ni feature) - `perf` - Optimisation de performance - `test` - Ajout ou modification de tests - `chore` - Maintenance (dépendances, config, etc.) **Exemples**: ```bash feat(auth): add biometric login support fix(dashboard): resolve null pointer on stats refresh docs(readme): update installation instructions refactor(cache): simplify cache service implementation test(members): add unit tests for member repository perf(dashboard): optimize dashboard loading time chore(deps): upgrade flutter to 3.5.4 ``` **Scope**: module concerné (auth, dashboard, members, etc.) **Description**: - Impératif ("add" pas "added" ou "adds") - Minuscule (pas de majuscule en début) - Pas de point final - Max 72 caractères --- ## Tests ### Tests Unitaires **Domain Layer**: ```dart // test/domain/usecases/get_dashboard_data_test.dart void main() { late GetDashboardData useCase; late MockDashboardRepository mockRepository; setUp(() { mockRepository = MockDashboardRepository(); useCase = GetDashboardData(mockRepository); }); test('should return DashboardEntity when repository call succeeds', () async { // Arrange when(mockRepository.getDashboardData()) .thenAnswer((_) async => Right(tDashboardEntity)); // Act final result = await useCase(); // Assert expect(result, Right(tDashboardEntity)); verify(mockRepository.getDashboardData()); verifyNoMoreInteractions(mockRepository); }); } ``` **BLoC Tests**: ```dart // test/presentation/bloc/dashboard_bloc_test.dart void main() { late DashboardBloc bloc; late MockGetDashboardData mockGetDashboardData; setUp(() { mockGetDashboardData = MockGetDashboardData(); bloc = DashboardBloc(getDashboardData: mockGetDashboardData); }); blocTest( 'emits [Loading, Loaded] when LoadDashboardData succeeds', build: () { when(mockGetDashboardData()).thenAnswer( (_) async => Right(tDashboard), ); return bloc; }, act: (bloc) => bloc.add(LoadDashboardData()), expect: () => [ DashboardLoading(), DashboardLoaded(tDashboard), ], verify: (_) { verify(mockGetDashboardData()).called(1); }, ); } ``` ### Lancer les Tests ```bash # Tous les tests flutter test # Tests avec coverage flutter test --coverage # Visualiser coverage genhtml coverage/lcov.info -o coverage/html open coverage/html/index.html ``` ### Coverage Minimum **Target**: 80% de coverage minimum pour les nouvelles features --- ## Pull Requests ### Avant de Créer une PR ✅ **Code compilé**: `flutter build apk --debug` ✅ **Linting**: `flutter analyze` sans erreur ✅ **Tests**: `flutter test` 100% pass ✅ **Formaté**: `flutter format lib/` ✅ **Commits propres**: Utiliser Conventional Commits ### Template de PR ```markdown ## Description Brève description du changement. ## Type de Changement - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update ## Issue Liée Closes #123 ## Comment Tester 1. Lancer l'app 2. Naviguer vers Dashboard 3. Vérifier que les stats se chargent ## Checklist - [ ] Mon code suit les conventions du projet - [ ] J'ai commenté mon code aux endroits complexes - [ ] J'ai mis à jour la documentation si nécessaire - [ ] Mes changements ne génèrent pas de warnings - [ ] J'ai ajouté des tests qui prouvent mon fix/feature - [ ] Tous les tests passent localement - [ ] J'ai vérifié que le code build en release ``` ### Taille de PR **Idéal**: 200-400 lignes **Max acceptable**: 800 lignes Si > 800 lignes, découper en plusieurs PRs. --- ## Code Review ### Pour le Reviewer ✅ **Architecture** - Respecte Clean Architecture + BLoC? ✅ **Tests** - Coverage suffisant? ✅ **Performance** - Pas de régressions? ✅ **Sécurité** - Pas de vulnérabilités? ✅ **Lisibilité** - Code compréhensible? ✅ **Documentation** - Suffisamment documenté? ### Pour l'Auteur ✅ **Répondre rapidement** aux commentaires ✅ **Argumenter** les choix techniques ✅ **Ne pas prendre personnellement** les critiques ✅ **Remercier** le reviewer ### Approuver une PR Nécessite **2 approbations** minimum: - 1 approbation d'un **Tech Lead** - 1 approbation d'un **Peer** --- ## Bonnes Pratiques ### DRY (Don't Repeat Yourself) ❌ **Mauvais**: ```dart // Dupliqué dans 5 fichiers final baseUrl = 'https://api.lions.dev'; ``` ✅ **Bon**: ```dart // core/config/environment.dart class AppConfig { static String get apiBaseUrl => _apiBaseUrl; } ``` ### KISS (Keep It Simple, Stupid) ❌ **Mauvais** (sur-ingénierie): ```dart class AbstractFactoryProviderManagerBuilderFactory { // 500 lignes de code complexe pour un simple getter } ``` ✅ **Bon**: ```dart class CacheService { T? get(String key) => _prefs.get(key); } ``` ### YAGNI (You Aren't Gonna Need It) ❌ **Mauvais** (fonctionnalités inutilisées): ```dart class User { // 50 champs dont 40 jamais utilisés } ``` ✅ **Bon**: ```dart class User { // Seulement les champs nécessaires final String id; final String email; final String name; } ``` ### Const Constructors ✅ **Toujours utiliser `const`** quand possible: ```dart const Text('Hello') const SizedBox(height: 16) const EdgeInsets.all(8) const Icon(Icons.home) ``` ### Null Safety ✅ **Utiliser les opérateurs null-safety**: ```dart final name = user?.name ?? 'Unknown'; final length = items?.length ?? 0; final result = await api.call() ?? defaultValue; ``` --- ## Questions? Si vous avez des questions: 1. **Lire la documentation** - README, ARCHITECTURE, cette doc 2. **Chercher dans les issues** - Peut-être déjà répondu 3. **Demander sur Discord** - #unionflow-dev channel 4. **Créer une issue** - Si toujours bloqué --- ## Ressources - [Flutter Docs](https://docs.flutter.dev/) - [Dart Style Guide](https://dart.dev/guides/language/effective-dart/style) - [BLoC Pattern](https://bloclibrary.dev/) - [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) --- **Merci de contribuer à UnionFlow Mobile!** 🎉 *Document maintenu par l'équipe UnionFlow Development Team*