feat(core): refonte architecture transverse (cache, network, websocket, DI)
- lib/app : app.dart, router mis à jour (routes nouveaux modules) - lib/core/cache : cache_service + cached_datasource_decorator - lib/core/network : api_client, offline_manager, retry_policy - lib/core/websocket : websocket service (reconnexion exponentielle, heartbeat) - lib/core/di : injection + register_module - lib/core/storage : pending_operations_store (offline support) - lib/core/navigation : main_navigation_layout (onglets par rôle) - lib/core/config : environment, lcb_ft_constants - lib/core/utils : error_formatter, validators - pubspec.yaml/lock : dépendances mises à jour
This commit is contained in:
@@ -46,7 +46,13 @@ class UnionFlowApp extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
child: Consumer2<LocaleProvider, ThemeProvider>(
|
child: Consumer2<LocaleProvider, ThemeProvider>(
|
||||||
builder: (context, locale, theme, child) {
|
builder: (context, locale, theme, child) {
|
||||||
return MaterialApp(
|
return BlocListener<AuthBloc, AuthState>(
|
||||||
|
listenWhen: (prev, curr) =>
|
||||||
|
curr is AuthAuthenticated && prev is! AuthAuthenticated,
|
||||||
|
listener: (context, _) {
|
||||||
|
context.read<OrgSwitcherBloc>().add(const OrgSwitcherLoadRequested());
|
||||||
|
},
|
||||||
|
child: MaterialApp(
|
||||||
title: 'UnionFlow',
|
title: 'UnionFlow',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
scaffoldMessengerKey: UnionFlowApp.scaffoldMessengerKey,
|
scaffoldMessengerKey: UnionFlowApp.scaffoldMessengerKey,
|
||||||
@@ -79,6 +85,7 @@ class UnionFlowApp extends StatelessWidget {
|
|||||||
child: child ?? const SizedBox(),
|
child: child ?? const SizedBox(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import '../../features/adhesions/presentation/pages/adhesions_page_wrapper.dart'
|
|||||||
import '../../features/settings/presentation/pages/system_settings_page.dart';
|
import '../../features/settings/presentation/pages/system_settings_page.dart';
|
||||||
import '../../features/dashboard/presentation/pages/advanced_dashboard_page.dart';
|
import '../../features/dashboard/presentation/pages/advanced_dashboard_page.dart';
|
||||||
import '../../features/admin/presentation/pages/user_management_page.dart';
|
import '../../features/admin/presentation/pages/user_management_page.dart';
|
||||||
import '../../features/communication/presentation/pages/conversations_page.dart';
|
import '../../features/communication/presentation/pages/conversations_page_wrapper.dart';
|
||||||
import '../../features/finance_workflow/presentation/pages/pending_approvals_page.dart';
|
import '../../features/finance_workflow/presentation/pages/pending_approvals_page.dart';
|
||||||
import '../../features/finance_workflow/presentation/pages/budgets_list_page.dart';
|
import '../../features/finance_workflow/presentation/pages/budgets_list_page.dart';
|
||||||
import '../../core/navigation/main_navigation_layout.dart';
|
import '../../core/navigation/main_navigation_layout.dart';
|
||||||
@@ -86,7 +86,7 @@ class AppRouter {
|
|||||||
'/reports': (context) => const ReportsPageWrapper(),
|
'/reports': (context) => const ReportsPageWrapper(),
|
||||||
'/finances': (context) => const CotisationsPageWrapper(),
|
'/finances': (context) => const CotisationsPageWrapper(),
|
||||||
'/adhesions': (context) => const AdhesionsPageWrapper(),
|
'/adhesions': (context) => const AdhesionsPageWrapper(),
|
||||||
'/messages': (context) => const ConversationsPage(),
|
'/messages': (context) => const ConversationsPageWrapper(),
|
||||||
'/settings': (context) => const SystemSettingsPage(),
|
'/settings': (context) => const SystemSettingsPage(),
|
||||||
'/analytics': (context) {
|
'/analytics': (context) {
|
||||||
final authState = context.read<AuthBloc>().state;
|
final authState = context.read<AuthBloc>().state;
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class _MainNavigationLayoutState extends State<MainNavigationLayout> {
|
|||||||
statusBarIconBrightness: Brightness.dark,
|
statusBarIconBrightness: Brightness.dark,
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: ColorTokens.background,
|
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: true,
|
top: true,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
@@ -199,12 +199,13 @@ class _PillNavigationBar extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final scheme = Theme.of(context).colorScheme;
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorTokens.surface,
|
color: scheme.surface,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorTokens.shadow,
|
color: scheme.shadow.withOpacity(0.12),
|
||||||
blurRadius: 12,
|
blurRadius: 12,
|
||||||
offset: const Offset(0, -2),
|
offset: const Offset(0, -2),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../shared/design_system/tokens/app_colors.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import '../../app/app.dart';
|
import '../../app/app.dart';
|
||||||
@@ -35,17 +36,7 @@ class ApiClient {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Intercepteur de Log (Uniquement en Dev)
|
// Intercepteur de Token & Refresh automatique (doit être AVANT le logger)
|
||||||
if (AppConfig.enableLogging) {
|
|
||||||
_dio.interceptors.add(LogInterceptor(
|
|
||||||
requestHeader: true,
|
|
||||||
requestBody: true,
|
|
||||||
responseBody: true,
|
|
||||||
logPrint: (obj) => print('🌐 [API] $obj'),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intercepteur de Token & Refresh automatique
|
|
||||||
_dio.interceptors.add(
|
_dio.interceptors.add(
|
||||||
InterceptorsWrapper(
|
InterceptorsWrapper(
|
||||||
onRequest: (options, handler) async {
|
onRequest: (options, handler) async {
|
||||||
@@ -112,6 +103,16 @@ class ApiClient {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Intercepteur de Log (après le token pour voir le Bearer dans les logs)
|
||||||
|
if (AppConfig.enableLogging) {
|
||||||
|
_dio.interceptors.add(LogInterceptor(
|
||||||
|
requestHeader: true,
|
||||||
|
requestBody: true,
|
||||||
|
responseBody: true,
|
||||||
|
logPrint: (obj) => print('🌐 [API] $obj'),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _forceLogout() {
|
void _forceLogout() {
|
||||||
@@ -132,7 +133,7 @@ class ApiClient {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.orange.shade700,
|
backgroundColor: AppColors.warning,
|
||||||
duration: const Duration(seconds: 4),
|
duration: const Duration(seconds: 4),
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -77,6 +77,17 @@ abstract class WebSocketEvent {
|
|||||||
organizationId: json['organizationId'] as String?,
|
organizationId: json['organizationId'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case 'NOUVEAU_MESSAGE':
|
||||||
|
case 'MESSAGE_SUPPRIME':
|
||||||
|
case 'CONVERSATION_LUE':
|
||||||
|
return ChatMessageEvent(
|
||||||
|
eventType: eventType,
|
||||||
|
timestamp: timestamp,
|
||||||
|
data: data,
|
||||||
|
conversationId: json['conversationId'] as String?,
|
||||||
|
organizationId: json['organizationId'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return GenericEvent(
|
return GenericEvent(
|
||||||
eventType: eventType,
|
eventType: eventType,
|
||||||
@@ -144,6 +155,19 @@ class ContributionEvent extends WebSocketEvent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChatMessageEvent extends WebSocketEvent {
|
||||||
|
final String? conversationId;
|
||||||
|
final String? organizationId;
|
||||||
|
|
||||||
|
ChatMessageEvent({
|
||||||
|
required super.eventType,
|
||||||
|
required super.timestamp,
|
||||||
|
required super.data,
|
||||||
|
this.conversationId,
|
||||||
|
this.organizationId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class GenericEvent extends WebSocketEvent {
|
class GenericEvent extends WebSocketEvent {
|
||||||
GenericEvent({
|
GenericEvent({
|
||||||
required super.eventType,
|
required super.eventType,
|
||||||
|
|||||||
56
pubspec.lock
56
pubspec.lock
@@ -17,6 +17,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.1"
|
version: "8.4.1"
|
||||||
|
ansicolor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ansicolor
|
||||||
|
sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -177,6 +185,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
|
cli_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_util
|
||||||
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -249,6 +265,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.7"
|
version: "3.0.7"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
csv:
|
csv:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -451,6 +475,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_launcher_icons:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_launcher_icons
|
||||||
|
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.1"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -488,6 +520,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_native_splash:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_native_splash
|
||||||
|
sha256: "7062602e0dbd29141fb8eb19220b5871ca650be5197ab9c1f193a28b17537bc7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.4"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -623,6 +663,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.3.2"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.6"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1489,6 +1537,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
universal_io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: universal_io
|
||||||
|
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
url_launcher:
|
url_launcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
37
pubspec.yaml
37
pubspec.yaml
@@ -83,12 +83,49 @@ dev_dependencies:
|
|||||||
bloc_test: ^9.1.7
|
bloc_test: ^9.1.7
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_launcher_icons: ^0.13.1
|
||||||
|
flutter_native_splash: ^2.4.1
|
||||||
|
|
||||||
|
# ─── Génération des icônes d'application (launcher) ──────────────────────────
|
||||||
|
# Source unique : assets/images/unionflow-logo.png
|
||||||
|
# Régénérer : dart run flutter_launcher_icons
|
||||||
|
flutter_launcher_icons:
|
||||||
|
android: "ic_launcher"
|
||||||
|
ios: true
|
||||||
|
remove_alpha_ios: true
|
||||||
|
image_path: "assets/images/unionflow-logo.png"
|
||||||
|
background_color_ios: "#FFFFFF"
|
||||||
|
min_sdk_android: 21
|
||||||
|
adaptive_icon_background: "#FFFFFF"
|
||||||
|
adaptive_icon_foreground: "assets/images/unionflow-logo.png"
|
||||||
|
web:
|
||||||
|
generate: false
|
||||||
|
windows:
|
||||||
|
generate: false
|
||||||
|
macos:
|
||||||
|
generate: false
|
||||||
|
|
||||||
|
# ─── Génération du splash screen natif (Android + iOS) ───────────────────────
|
||||||
|
# Régénérer : dart run flutter_native_splash:create
|
||||||
|
flutter_native_splash:
|
||||||
|
color: "#FFFFFF"
|
||||||
|
color_dark: "#0A0D1A"
|
||||||
|
image: "assets/images/unionflow-logo.png"
|
||||||
|
android_12:
|
||||||
|
image: "assets/images/unionflow-logo.png"
|
||||||
|
color: "#FFFFFF"
|
||||||
|
color_dark: "#0A0D1A"
|
||||||
|
android: true
|
||||||
|
ios: true
|
||||||
|
web: false
|
||||||
|
fullscreen: false
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
generate: true
|
generate: true
|
||||||
assets:
|
assets:
|
||||||
- assets/images/
|
- assets/images/
|
||||||
|
- assets/images/branding/
|
||||||
- assets/images/payment_methods/wave/
|
- assets/images/payment_methods/wave/
|
||||||
- assets/images/payment_methods/orange_money/
|
- assets/images/payment_methods/orange_money/
|
||||||
- assets/images/payment_methods/free_money/
|
- assets/images/payment_methods/free_money/
|
||||||
|
|||||||
Reference in New Issue
Block a user