Initial commit: unionflow-mobile-apps

Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 16:30:08 +00:00
commit d094d6db9c
1790 changed files with 507435 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
import 'package:equatable/equatable.dart';
class CompteAdherentEntity extends Equatable {
final String numeroMembre;
final String nomComplet;
final String? organisationNom;
final DateTime? dateAdhesion;
final String statutCompte;
final double soldeCotisations;
final double soldeEpargne;
final double soldeBloque;
final double soldeTotalDisponible;
final double encoursCreditTotal;
final double capaciteEmprunt;
final int nombreCotisationsPayees;
final int nombreCotisationsTotal;
final int nombreCotisationsEnRetard;
final double engagementRate;
final int nombreComptesEpargne;
final DateTime dateCalcul;
const CompteAdherentEntity({
required this.numeroMembre,
required this.nomComplet,
this.organisationNom,
this.dateAdhesion,
required this.statutCompte,
required this.soldeCotisations,
required this.soldeEpargne,
required this.soldeBloque,
required this.soldeTotalDisponible,
required this.encoursCreditTotal,
required this.capaciteEmprunt,
required this.nombreCotisationsPayees,
required this.nombreCotisationsTotal,
required this.nombreCotisationsEnRetard,
required this.engagementRate,
required this.nombreComptesEpargne,
required this.dateCalcul,
});
@override
List<Object?> get props => [
numeroMembre,
nomComplet,
organisationNom,
dateAdhesion,
statutCompte,
soldeCotisations,
soldeEpargne,
soldeBloque,
soldeTotalDisponible,
encoursCreditTotal,
capaciteEmprunt,
nombreCotisationsPayees,
nombreCotisationsTotal,
nombreCotisationsEnRetard,
engagementRate,
nombreComptesEpargne,
dateCalcul,
];
}

View File

@@ -0,0 +1,261 @@
import 'package:equatable/equatable.dart';
import 'compte_adherent_entity.dart';
/// Entité pour les statistiques du dashboard
class DashboardStatsEntity extends Equatable {
final int totalMembers;
final int activeMembers;
final int totalEvents;
final int upcomingEvents;
final int totalContributions;
final double totalContributionAmount;
/// Montant des cotisations seules (sans épargne), pour la carte « Contribution Totale » membre.
final double? contributionsAmountOnly;
final int pendingRequests;
final int completedProjects;
final double monthlyGrowth;
final double engagementRate;
final DateTime lastUpdated;
final int? totalOrganizations;
final Map<String, int>? organizationTypeDistribution;
const DashboardStatsEntity({
required this.totalMembers,
required this.activeMembers,
required this.totalEvents,
required this.upcomingEvents,
required this.totalContributions,
required this.totalContributionAmount,
this.contributionsAmountOnly,
required this.pendingRequests,
required this.completedProjects,
required this.monthlyGrowth,
required this.engagementRate,
required this.lastUpdated,
this.totalOrganizations,
this.organizationTypeDistribution,
});
// Méthodes utilitaires
double get memberActivityRate => totalMembers > 0 ? activeMembers / totalMembers : 0.0;
bool get hasGrowth => monthlyGrowth > 0;
bool get isHighEngagement => engagementRate > 0.7;
String get formattedContributionAmount {
if (totalContributionAmount >= 1000000) {
return '${(totalContributionAmount / 1000000).toStringAsFixed(1)}M';
} else if (totalContributionAmount >= 1000) {
return '${(totalContributionAmount / 1000).toStringAsFixed(1)}K';
}
return totalContributionAmount.toStringAsFixed(0);
}
@override
List<Object?> get props => [
totalMembers,
activeMembers,
totalEvents,
upcomingEvents,
totalContributions,
totalContributionAmount,
contributionsAmountOnly,
pendingRequests,
completedProjects,
monthlyGrowth,
engagementRate,
lastUpdated,
totalOrganizations,
organizationTypeDistribution,
];
}
/// Entité pour les activités récentes
class RecentActivityEntity extends Equatable {
final String id;
final String type;
final String title;
final String description;
final String? userAvatar;
final String userName;
final DateTime timestamp;
final String? actionUrl;
final Map<String, dynamic>? metadata;
const RecentActivityEntity({
required this.id,
required this.type,
required this.title,
required this.description,
this.userAvatar,
required this.userName,
required this.timestamp,
this.actionUrl,
this.metadata,
});
// Méthodes utilitaires
String get timeAgo {
final now = DateTime.now();
final difference = now.difference(timestamp);
if (difference.inDays > 0) {
return '${difference.inDays}j';
} else if (difference.inHours > 0) {
return '${difference.inHours}h';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes}min';
} else {
return 'maintenant';
}
}
bool get isRecent => DateTime.now().difference(timestamp).inHours < 24;
bool get hasAction => actionUrl != null && actionUrl!.isNotEmpty;
@override
List<Object?> get props => [
id,
type,
title,
description,
userAvatar,
userName,
timestamp,
actionUrl,
metadata,
];
}
/// Entité pour les événements à venir
class UpcomingEventEntity extends Equatable {
final String id;
final String title;
final String description;
final DateTime startDate;
final DateTime? endDate;
final String location;
final int maxParticipants;
final int currentParticipants;
final String status;
final String? imageUrl;
final List<String> tags;
const UpcomingEventEntity({
required this.id,
required this.title,
required this.description,
required this.startDate,
this.endDate,
required this.location,
required this.maxParticipants,
required this.currentParticipants,
required this.status,
this.imageUrl,
required this.tags,
});
// Méthodes utilitaires
bool get isAlmostFull => currentParticipants >= (maxParticipants * 0.8);
bool get isFull => currentParticipants >= maxParticipants;
double get fillPercentage => maxParticipants > 0 ? currentParticipants / maxParticipants : 0.0;
int get daysUntilEventInt {
final now = DateTime.now();
final difference = startDate.difference(now);
return difference.inDays;
}
String get daysUntilEvent {
final now = DateTime.now();
final difference = startDate.difference(now);
if (difference.inDays > 0) {
return '${difference.inDays} jour${difference.inDays > 1 ? 's' : ''}';
} else if (difference.inHours > 0) {
return '${difference.inHours}h';
} else if (difference.inMinutes > 0) {
return '${difference.inMinutes}min';
} else {
return 'En cours';
}
}
String get formattedDate {
final months = ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun',
'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc'];
return '${startDate.day} ${months[startDate.month - 1]} ${startDate.year}';
}
bool get hasParticipantInfo => maxParticipants > 0;
bool get isToday {
final now = DateTime.now();
return startDate.year == now.year &&
startDate.month == now.month &&
startDate.day == now.day;
}
bool get isTomorrow {
final tomorrow = DateTime.now().add(const Duration(days: 1));
return startDate.year == tomorrow.year &&
startDate.month == tomorrow.month &&
startDate.day == tomorrow.day;
}
@override
List<Object?> get props => [
id,
title,
description,
startDate,
endDate,
location,
maxParticipants,
currentParticipants,
status,
imageUrl,
tags,
];
}
/// Entité principale du dashboard
class DashboardEntity extends Equatable {
final DashboardStatsEntity stats;
final List<RecentActivityEntity> recentActivities;
final List<UpcomingEventEntity> upcomingEvents;
final Map<String, dynamic> userPreferences;
final String organizationId;
final String userId;
/// Compte adhérent unifié (si disponible)
final CompteAdherentEntity? monCompte;
const DashboardEntity({
required this.stats,
required this.recentActivities,
required this.upcomingEvents,
required this.userPreferences,
required this.organizationId,
required this.userId,
this.monCompte,
});
// Méthodes utilitaires
bool get hasRecentActivity => recentActivities.isNotEmpty;
bool get hasUpcomingEvents => upcomingEvents.isNotEmpty;
int get todayEventsCount => upcomingEvents.where((e) => e.isToday).length;
int get tomorrowEventsCount => upcomingEvents.where((e) => e.isTomorrow).length;
int get recentActivitiesCount => recentActivities.length;
@override
List<Object?> get props => [
stats,
recentActivities,
upcomingEvents,
userPreferences,
organizationId,
userId,
monCompte,
];
}

View File

@@ -0,0 +1,31 @@
import 'package:dartz/dartz.dart';
import '../entities/dashboard_entity.dart';
import '../entities/compte_adherent_entity.dart';
import '../../../../core/error/failures.dart';
abstract class DashboardRepository {
/// Récupère le compte adhérent unifié (soldes, crédits, capacité d'emprunt).
Future<Either<Failure, CompteAdherentEntity>> getCompteAdherent();
Future<Either<Failure, DashboardEntity>> getDashboardData(
String organizationId,
String userId,
);
Future<Either<Failure, DashboardStatsEntity>> getDashboardStats(
String organizationId,
String userId,
);
Future<Either<Failure, List<RecentActivityEntity>>> getRecentActivities(
String organizationId,
String userId, {
int limit = 10,
});
Future<Either<Failure, List<UpcomingEventEntity>>> getUpcomingEvents(
String organizationId,
String userId, {
int limit = 5,
});
}

View File

@@ -0,0 +1,18 @@
import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/usecases/usecase.dart';
import '../entities/compte_adherent_entity.dart';
import '../repositories/dashboard_repository.dart';
@injectable
class GetCompteAdherent implements UseCase<CompteAdherentEntity, NoParams> {
final DashboardRepository repository;
GetCompteAdherent(this.repository);
@override
Future<Either<Failure, CompteAdherentEntity>> call(NoParams params) async {
return await repository.getCompteAdherent();
}
}

View File

@@ -0,0 +1,125 @@
import 'package:injectable/injectable.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import '../entities/dashboard_entity.dart';
import '../repositories/dashboard_repository.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/usecases/usecase.dart';
@injectable
class GetDashboardData implements UseCase<DashboardEntity, GetDashboardDataParams> {
final DashboardRepository repository;
GetDashboardData(this.repository);
@override
Future<Either<Failure, DashboardEntity>> call(GetDashboardDataParams params) async {
return await repository.getDashboardData(
params.organizationId,
params.userId,
);
}
}
class GetDashboardDataParams extends Equatable {
final String organizationId;
final String userId;
const GetDashboardDataParams({
required this.organizationId,
required this.userId,
});
@override
List<Object> get props => [organizationId, userId];
}
@injectable
class GetDashboardStats implements UseCase<DashboardStatsEntity, GetDashboardStatsParams> {
final DashboardRepository repository;
GetDashboardStats(this.repository);
@override
Future<Either<Failure, DashboardStatsEntity>> call(GetDashboardStatsParams params) async {
return await repository.getDashboardStats(
params.organizationId,
params.userId,
);
}
}
class GetDashboardStatsParams extends Equatable {
final String organizationId;
final String userId;
const GetDashboardStatsParams({
required this.organizationId,
required this.userId,
});
@override
List<Object> get props => [organizationId, userId];
}
@injectable
class GetRecentActivities implements UseCase<List<RecentActivityEntity>, GetRecentActivitiesParams> {
final DashboardRepository repository;
GetRecentActivities(this.repository);
@override
Future<Either<Failure, List<RecentActivityEntity>>> call(GetRecentActivitiesParams params) async {
return await repository.getRecentActivities(
params.organizationId,
params.userId,
limit: params.limit,
);
}
}
class GetRecentActivitiesParams extends Equatable {
final String organizationId;
final String userId;
final int limit;
const GetRecentActivitiesParams({
required this.organizationId,
required this.userId,
this.limit = 10,
});
@override
List<Object> get props => [organizationId, userId, limit];
}
@injectable
class GetUpcomingEvents implements UseCase<List<UpcomingEventEntity>, GetUpcomingEventsParams> {
final DashboardRepository repository;
GetUpcomingEvents(this.repository);
@override
Future<Either<Failure, List<UpcomingEventEntity>>> call(GetUpcomingEventsParams params) async {
return await repository.getUpcomingEvents(
params.organizationId,
params.userId,
limit: params.limit,
);
}
}
class GetUpcomingEventsParams extends Equatable {
final String organizationId;
final String userId;
final int limit;
const GetUpcomingEventsParams({
required this.organizationId,
required this.userId,
this.limit = 5,
});
@override
List<Object> get props => [organizationId, userId, limit];
}