feat(mobile): Contribution Totale + KPI dashboard membre

- MembreDashboardSyntheseModel: totalCotisationsPayeesToutTemps
- DashboardStatsEntity: contributionsAmountOnly (cotisations seules)
- Mapping: Mon Solde Total = cotisations tout temps + épargne, Contribution Totale = cotisations seules
- Engagement: fallback tauxCotisationsPerso si tauxParticipation absent
- Carte Contribution Totale utilise contributionsAmountOnly

Made-with: Cursor
This commit is contained in:
dahoud
2026-03-09 19:58:39 +00:00
parent 0a9dece955
commit 553e731a51
4 changed files with 613 additions and 260 deletions

View File

@@ -1,12 +1,15 @@
import 'package:injectable/injectable.dart';
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_entity.dart';
import '../../domain/repositories/dashboard_repository.dart';
import '../datasources/dashboard_remote_datasource.dart';
import '../models/dashboard_stats_model.dart';
import '../models/membre_dashboard_synthese_model.dart';
import '../../../../core/error/exceptions.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/network/network_info.dart';
@LazySingleton(as: DashboardRepository)
class DashboardRepositoryImpl implements DashboardRepository {
final DashboardRemoteDataSource remoteDataSource;
final NetworkInfo networkInfo;
@@ -21,18 +24,55 @@ class DashboardRepositoryImpl implements DashboardRepository {
String organizationId,
String userId,
) async {
if (await networkInfo.isConnected) {
try {
final dashboardData = await remoteDataSource.getDashboardData(organizationId, userId);
return Right(_mapToEntity(dashboardData));
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} catch (e) {
return Left(ServerFailure('Unexpected error: $e'));
}
} else {
if (!await networkInfo.isConnected) {
return const Left(NetworkFailure('No internet connection'));
}
try {
// Membre sans contexte org : utiliser l'API dashboard membre (GET /api/dashboard/membre/me)
final useMemberDashboard = organizationId.trim().isEmpty;
if (useMemberDashboard) {
final synthese = await remoteDataSource.getMemberDashboardData();
return Right(_mapMemberSyntheseToEntity(synthese, userId));
}
final dashboardData = await remoteDataSource.getDashboardData(organizationId, userId);
return Right(_mapToEntity(dashboardData));
} on ServerException catch (e) {
return Left(ServerFailure(e.message));
} catch (e) {
return Left(ServerFailure('Unexpected error: $e'));
}
}
/// Construit une DashboardEntity à partir de la synthèse membre (même structure pour réutiliser l'UI).
DashboardEntity _mapMemberSyntheseToEntity(MembreDashboardSyntheseModel s, String userId) {
final now = DateTime.now();
// Contribution Totale = cotisations payées tout temps ; MON SOLDE TOTAL = cotisations tout temps + épargne
final totalCotisationsToutTemps = s.totalCotisationsPayeesToutTemps;
final monSoldeTotal = totalCotisationsToutTemps + s.monSoldeEpargne;
final stats = DashboardStatsEntity(
totalMembers: 0,
activeMembers: 0,
totalEvents: 0,
upcomingEvents: s.evenementsAVenir,
totalContributions: s.nombreCotisationsPayees,
totalContributionAmount: monSoldeTotal,
contributionsAmountOnly: totalCotisationsToutTemps,
pendingRequests: 0,
completedProjects: 0,
monthlyGrowth: s.evolutionEpargneNombre,
engagementRate: ((s.tauxParticipationPerso ?? s.tauxCotisationsPerso) ?? 0) / 100.0,
lastUpdated: now,
totalOrganizations: null,
organizationTypeDistribution: null,
);
return DashboardEntity(
stats: stats,
recentActivities: const [],
upcomingEvents: const [],
userPreferences: <String, dynamic>{},
organizationId: '',
userId: userId,
);
}
@override
@@ -122,11 +162,14 @@ class DashboardRepositoryImpl implements DashboardRepository {
upcomingEvents: model.upcomingEvents,
totalContributions: model.totalContributions,
totalContributionAmount: model.totalContributionAmount,
contributionsAmountOnly: null,
pendingRequests: model.pendingRequests,
completedProjects: model.completedProjects,
monthlyGrowth: model.monthlyGrowth,
engagementRate: model.engagementRate,
lastUpdated: model.lastUpdated,
totalOrganizations: null,
organizationTypeDistribution: null,
);
}