docs(mobile): documentation complète Spec 001 + architecture
Documentation ajoutée : - ARCHITECTURE.md : Clean Architecture par feature, BLoC pattern - OPTIMISATIONS_PERFORMANCE.md : Cache multi-niveaux, pagination, lazy loading - SECURITE_PRODUCTION.md : FlutterSecureStorage, JWT, HTTPS, ProGuard - CHANGELOG.md : Historique versions - CONTRIBUTING.md : Guide contribution - README.md : Mise à jour (build, env config) Widgets partagés : - file_upload_widget.dart : Upload fichiers (photos/PDFs) Cache : - lib/core/cache/ : Système cache L1/L2 (mémoire/disque) Dependencies : - pubspec.yaml : file_picker 8.1.2, injectable, dio Spec 001 : 27/27 tâches (100%) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
582
CONTRIBUTING.md
Normal file
582
CONTRIBUTING.md
Normal file
@@ -0,0 +1,582 @@
|
||||
# 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<User?> 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/<issue-number>-<short-description>
|
||||
```
|
||||
|
||||
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**:
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
[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<DashboardBloc, DashboardState>(
|
||||
'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<T>(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*
|
||||
Reference in New Issue
Block a user