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:
dahoud
2026-04-15 20:26:20 +00:00
parent 3a2c8a808f
commit 07b8488714
7 changed files with 145 additions and 19 deletions

View File

@@ -118,7 +118,7 @@ class _MainNavigationLayoutState extends State<MainNavigationLayout> {
statusBarIconBrightness: Brightness.dark,
),
child: Scaffold(
backgroundColor: ColorTokens.background,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: SafeArea(
top: true,
bottom: false,
@@ -199,12 +199,13 @@ class _PillNavigationBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: ColorTokens.surface,
color: scheme.surface,
boxShadow: [
BoxShadow(
color: ColorTokens.shadow,
color: scheme.shadow.withOpacity(0.12),
blurRadius: 12,
offset: const Offset(0, -2),
),

View File

@@ -1,6 +1,7 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../shared/design_system/tokens/app_colors.dart';
import 'package:injectable/injectable.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../../app/app.dart';
@@ -35,17 +36,7 @@ class ApiClient {
),
);
// Intercepteur de Log (Uniquement en Dev)
if (AppConfig.enableLogging) {
_dio.interceptors.add(LogInterceptor(
requestHeader: true,
requestBody: true,
responseBody: true,
logPrint: (obj) => print('🌐 [API] $obj'),
));
}
// Intercepteur de Token & Refresh automatique
// Intercepteur de Token & Refresh automatique (doit être AVANT le logger)
_dio.interceptors.add(
InterceptorsWrapper(
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() {
@@ -132,7 +133,7 @@ class ApiClient {
),
],
),
backgroundColor: Colors.orange.shade700,
backgroundColor: AppColors.warning,
duration: const Duration(seconds: 4),
behavior: SnackBarBehavior.floating,
),

View File

@@ -77,6 +77,17 @@ abstract class WebSocketEvent {
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:
return GenericEvent(
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 {
GenericEvent({
required super.eventType,