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,129 @@
/// BLoC pour la gestion des approbations de transactions
library approval_bloc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../domain/usecases/get_pending_approvals.dart';
import '../../domain/usecases/approve_transaction.dart';
import '../../domain/usecases/reject_transaction.dart';
import '../../domain/usecases/get_approval_by_id.dart';
import 'approval_event.dart';
import 'approval_state.dart';
@injectable
class ApprovalBloc extends Bloc<ApprovalEvent, ApprovalState> {
final GetPendingApprovals getPendingApprovals;
final GetApprovalById getApprovalById;
final ApproveTransaction approveTransaction;
final RejectTransaction rejectTransaction;
ApprovalBloc({
required this.getPendingApprovals,
required this.getApprovalById,
required this.approveTransaction,
required this.rejectTransaction,
}) : super(const ApprovalInitial()) {
on<LoadPendingApprovals>(_onLoadPendingApprovals);
on<LoadApprovalById>(_onLoadApprovalById);
on<ApproveTransactionEvent>(_onApproveTransaction);
on<RejectTransactionEvent>(_onRejectTransaction);
on<RefreshApprovals>(_onRefreshApprovals);
}
Future<void> _onLoadPendingApprovals(
LoadPendingApprovals event,
Emitter<ApprovalState> emit,
) async {
emit(const ApprovalsLoading());
final result = await getPendingApprovals(
organizationId: event.organizationId,
);
result.fold(
(failure) => emit(ApprovalError(failure.message)),
(approvals) {
if (approvals.isEmpty) {
emit(const ApprovalsEmpty());
} else {
emit(ApprovalsLoaded(
approvals: approvals,
pendingCount: approvals.length,
));
}
},
);
}
Future<void> _onLoadApprovalById(
LoadApprovalById event,
Emitter<ApprovalState> emit,
) async {
emit(const ApprovalsLoading());
final result = await getApprovalById(event.approvalId);
result.fold(
(failure) => emit(ApprovalError(failure.message)),
(approval) => emit(ApprovalDetailLoaded(approval)),
);
}
Future<void> _onApproveTransaction(
ApproveTransactionEvent event,
Emitter<ApprovalState> emit,
) async {
emit(const ApprovalActionInProgress('approve'));
final result = await approveTransaction(
approvalId: event.approvalId,
comment: event.comment,
);
result.fold(
(failure) => emit(ApprovalError(failure.message)),
(approval) => emit(TransactionApproved(approval: approval)),
);
}
Future<void> _onRejectTransaction(
RejectTransactionEvent event,
Emitter<ApprovalState> emit,
) async {
emit(const ApprovalActionInProgress('reject'));
final result = await rejectTransaction(
approvalId: event.approvalId,
reason: event.reason,
);
result.fold(
(failure) => emit(ApprovalError(failure.message)),
(approval) => emit(TransactionRejected(approval: approval)),
);
}
Future<void> _onRefreshApprovals(
RefreshApprovals event,
Emitter<ApprovalState> emit,
) async {
// Keep current state while refreshing
final result = await getPendingApprovals(
organizationId: event.organizationId,
);
result.fold(
(failure) => emit(ApprovalError(failure.message)),
(approvals) {
if (approvals.isEmpty) {
emit(const ApprovalsEmpty());
} else {
emit(ApprovalsLoaded(
approvals: approvals,
pendingCount: approvals.length,
));
}
},
);
}
}

View File

@@ -0,0 +1,69 @@
/// Événements pour le BLoC des approbations
library approval_event;
import 'package:equatable/equatable.dart';
abstract class ApprovalEvent extends Equatable {
const ApprovalEvent();
@override
List<Object?> get props => [];
}
/// Charger les approbations en attente
class LoadPendingApprovals extends ApprovalEvent {
final String? organizationId;
const LoadPendingApprovals({this.organizationId});
@override
List<Object?> get props => [organizationId];
}
/// Charger une approbation spécifique
class LoadApprovalById extends ApprovalEvent {
final String approvalId;
const LoadApprovalById(this.approvalId);
@override
List<Object?> get props => [approvalId];
}
/// Approuver une transaction
class ApproveTransactionEvent extends ApprovalEvent {
final String approvalId;
final String? comment;
const ApproveTransactionEvent({
required this.approvalId,
this.comment,
});
@override
List<Object?> get props => [approvalId, comment];
}
/// Rejeter une transaction
class RejectTransactionEvent extends ApprovalEvent {
final String approvalId;
final String reason;
const RejectTransactionEvent({
required this.approvalId,
required this.reason,
});
@override
List<Object?> get props => [approvalId, reason];
}
/// Rafraîchir les approbations
class RefreshApprovals extends ApprovalEvent {
final String? organizationId;
const RefreshApprovals({this.organizationId});
@override
List<Object?> get props => [organizationId];
}

View File

@@ -0,0 +1,106 @@
/// États pour le BLoC des approbations
library approval_state;
import 'package:equatable/equatable.dart';
import '../../domain/entities/transaction_approval.dart';
abstract class ApprovalState extends Equatable {
const ApprovalState();
@override
List<Object?> get props => [];
}
/// État initial
class ApprovalInitial extends ApprovalState {
const ApprovalInitial();
}
/// Chargement en cours
class ApprovalsLoading extends ApprovalState {
const ApprovalsLoading();
}
/// Approbations chargées
class ApprovalsLoaded extends ApprovalState {
final List<TransactionApproval> approvals;
final int pendingCount;
const ApprovalsLoaded({
required this.approvals,
required this.pendingCount,
});
@override
List<Object?> get props => [approvals, pendingCount];
}
/// Approbation spécifique chargée
class ApprovalDetailLoaded extends ApprovalState {
final TransactionApproval approval;
const ApprovalDetailLoaded(this.approval);
@override
List<Object?> get props => [approval];
}
/// Transaction approuvée avec succès
class TransactionApproved extends ApprovalState {
final TransactionApproval approval;
final String message;
const TransactionApproved({
required this.approval,
this.message = 'Transaction approuvée avec succès',
});
@override
List<Object?> get props => [approval, message];
}
/// Transaction rejetée avec succès
class TransactionRejected extends ApprovalState {
final TransactionApproval approval;
final String message;
const TransactionRejected({
required this.approval,
this.message = 'Transaction rejetée avec succès',
});
@override
List<Object?> get props => [approval, message];
}
/// Action en cours (approve/reject)
class ApprovalActionInProgress extends ApprovalState {
final String actionType; // 'approve' or 'reject'
const ApprovalActionInProgress(this.actionType);
@override
List<Object?> get props => [actionType];
}
/// Erreur
class ApprovalError extends ApprovalState {
final String message;
const ApprovalError(this.message);
@override
List<Object?> get props => [message];
}
/// Liste vide
class ApprovalsEmpty extends ApprovalState {
final String message;
const ApprovalsEmpty({
this.message = 'Aucune approbation en attente',
});
@override
List<Object?> get props => [message];
}

View File

@@ -0,0 +1,187 @@
/// BLoC pour la gestion des budgets
library budget_bloc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../domain/usecases/get_budgets.dart';
import '../../domain/usecases/get_budget_by_id.dart';
import '../../domain/usecases/create_budget.dart';
import '../../domain/usecases/get_budget_tracking.dart';
import 'budget_event.dart';
import 'budget_state.dart';
@injectable
class BudgetBloc extends Bloc<BudgetEvent, BudgetState> {
final GetBudgets getBudgets;
final GetBudgetById getBudgetById;
final CreateBudget createBudget;
final GetBudgetTracking getBudgetTracking;
BudgetBloc({
required this.getBudgets,
required this.getBudgetById,
required this.createBudget,
required this.getBudgetTracking,
}) : super(const BudgetInitial()) {
on<LoadBudgets>(_onLoadBudgets);
on<LoadBudgetById>(_onLoadBudgetById);
on<CreateBudgetEvent>(_onCreateBudget);
on<LoadBudgetTracking>(_onLoadBudgetTracking);
on<RefreshBudgets>(_onRefreshBudgets);
on<FilterBudgets>(_onFilterBudgets);
}
Future<void> _onLoadBudgets(
LoadBudgets event,
Emitter<BudgetState> emit,
) async {
emit(const BudgetsLoading());
final result = await getBudgets(
organizationId: event.organizationId,
status: event.status,
year: event.year,
);
result.fold(
(failure) => emit(BudgetError(failure.message)),
(budgets) {
if (budgets.isEmpty) {
emit(const BudgetsEmpty());
} else {
emit(BudgetsLoaded(
budgets: budgets,
filterStatus: event.status,
filterYear: event.year,
));
}
},
);
}
Future<void> _onLoadBudgetById(
LoadBudgetById event,
Emitter<BudgetState> emit,
) async {
emit(const BudgetsLoading());
final result = await getBudgetById(event.budgetId);
result.fold(
(failure) => emit(BudgetError(failure.message)),
(budget) => emit(BudgetDetailLoaded(budget)),
);
}
Future<void> _onCreateBudget(
CreateBudgetEvent event,
Emitter<BudgetState> emit,
) async {
emit(const BudgetActionInProgress('create'));
final result = await createBudget(
name: event.name,
description: event.description,
organizationId: event.organizationId,
period: event.period,
year: event.year,
month: event.month,
lines: event.lines,
);
result.fold(
(failure) => emit(BudgetError(failure.message)),
(budget) => emit(BudgetCreated(budget: budget)),
);
}
Future<void> _onLoadBudgetTracking(
LoadBudgetTracking event,
Emitter<BudgetState> emit,
) async {
emit(const BudgetsLoading());
// Load budget first
final budgetResult = await getBudgetById(event.budgetId);
await budgetResult.fold(
(failure) async => emit(BudgetError(failure.message)),
(budget) async {
// Then load tracking
final trackingResult = await getBudgetTracking(budgetId: event.budgetId);
trackingResult.fold(
(failure) => emit(BudgetError(failure.message)),
(tracking) => emit(BudgetTrackingLoaded(
budget: budget,
tracking: tracking,
)),
);
},
);
}
Future<void> _onRefreshBudgets(
RefreshBudgets event,
Emitter<BudgetState> emit,
) async {
final result = await getBudgets(
organizationId: event.organizationId,
status: event.status,
year: event.year,
);
result.fold(
(failure) => emit(BudgetError(failure.message)),
(budgets) {
if (budgets.isEmpty) {
emit(const BudgetsEmpty());
} else {
emit(BudgetsLoaded(
budgets: budgets,
filterStatus: event.status,
filterYear: event.year,
));
}
},
);
}
Future<void> _onFilterBudgets(
FilterBudgets event,
Emitter<BudgetState> emit,
) async {
// Keep current organization if in loaded state
String? organizationId;
if (state is BudgetsLoaded) {
final currentState = state as BudgetsLoaded;
// Extract org ID from first budget if available
if (currentState.budgets.isNotEmpty) {
organizationId = currentState.budgets.first.organizationId;
}
}
emit(const BudgetsLoading());
final result = await getBudgets(
organizationId: organizationId,
status: event.status,
year: event.year,
);
result.fold(
(failure) => emit(BudgetError(failure.message)),
(budgets) {
if (budgets.isEmpty) {
emit(const BudgetsEmpty());
} else {
emit(BudgetsLoaded(
budgets: budgets,
filterStatus: event.status,
filterYear: event.year,
));
}
},
);
}
}

View File

@@ -0,0 +1,110 @@
/// Événements pour le BLoC des budgets
library budget_event;
import 'package:equatable/equatable.dart';
import '../../domain/entities/budget.dart';
abstract class BudgetEvent extends Equatable {
const BudgetEvent();
@override
List<Object?> get props => [];
}
/// Charger les budgets
class LoadBudgets extends BudgetEvent {
final String? organizationId;
final BudgetStatus? status;
final int? year;
const LoadBudgets({
this.organizationId,
this.status,
this.year,
});
@override
List<Object?> get props => [organizationId, status, year];
}
/// Charger un budget spécifique
class LoadBudgetById extends BudgetEvent {
final String budgetId;
const LoadBudgetById(this.budgetId);
@override
List<Object?> get props => [budgetId];
}
/// Créer un budget
class CreateBudgetEvent extends BudgetEvent {
final String name;
final String? description;
final String organizationId;
final BudgetPeriod period;
final int year;
final int? month;
final List<BudgetLine> lines;
const CreateBudgetEvent({
required this.name,
this.description,
required this.organizationId,
required this.period,
required this.year,
this.month,
required this.lines,
});
@override
List<Object?> get props => [
name,
description,
organizationId,
period,
year,
month,
lines,
];
}
/// Charger le suivi budgétaire
class LoadBudgetTracking extends BudgetEvent {
final String budgetId;
const LoadBudgetTracking(this.budgetId);
@override
List<Object?> get props => [budgetId];
}
/// Rafraîchir les budgets
class RefreshBudgets extends BudgetEvent {
final String? organizationId;
final BudgetStatus? status;
final int? year;
const RefreshBudgets({
this.organizationId,
this.status,
this.year,
});
@override
List<Object?> get props => [organizationId, status, year];
}
/// Filtrer les budgets
class FilterBudgets extends BudgetEvent {
final BudgetStatus? status;
final int? year;
const FilterBudgets({
this.status,
this.year,
});
@override
List<Object?> get props => [status, year];
}

View File

@@ -0,0 +1,108 @@
/// États pour le BLoC des budgets
library budget_state;
import 'package:equatable/equatable.dart';
import '../../domain/entities/budget.dart';
abstract class BudgetState extends Equatable {
const BudgetState();
@override
List<Object?> get props => [];
}
/// État initial
class BudgetInitial extends BudgetState {
const BudgetInitial();
}
/// Chargement en cours
class BudgetsLoading extends BudgetState {
const BudgetsLoading();
}
/// Budgets chargés
class BudgetsLoaded extends BudgetState {
final List<Budget> budgets;
final BudgetStatus? filterStatus;
final int? filterYear;
const BudgetsLoaded({
required this.budgets,
this.filterStatus,
this.filterYear,
});
@override
List<Object?> get props => [budgets, filterStatus, filterYear];
}
/// Budget spécifique chargé
class BudgetDetailLoaded extends BudgetState {
final Budget budget;
const BudgetDetailLoaded(this.budget);
@override
List<Object?> get props => [budget];
}
/// Suivi budgétaire chargé
class BudgetTrackingLoaded extends BudgetState {
final Budget budget;
final Map<String, dynamic> tracking;
const BudgetTrackingLoaded({
required this.budget,
required this.tracking,
});
@override
List<Object?> get props => [budget, tracking];
}
/// Budget créé avec succès
class BudgetCreated extends BudgetState {
final Budget budget;
final String message;
const BudgetCreated({
required this.budget,
this.message = 'Budget créé avec succès',
});
@override
List<Object?> get props => [budget, message];
}
/// Action en cours (create, update)
class BudgetActionInProgress extends BudgetState {
final String actionType;
const BudgetActionInProgress(this.actionType);
@override
List<Object?> get props => [actionType];
}
/// Erreur
class BudgetError extends BudgetState {
final String message;
const BudgetError(this.message);
@override
List<Object?> get props => [message];
}
/// Liste vide
class BudgetsEmpty extends BudgetState {
final String message;
const BudgetsEmpty({
this.message = 'Aucun budget trouvé',
});
@override
List<Object?> get props => [message];
}