feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -0,0 +1,141 @@
import 'package:flutter/material.dart';
import '../tokens/app_colors.dart';
import '../tokens/app_typography.dart';
/// UnionFlow Mobile App - Thème Global
/// Utilise la charte stricte (Vert/Blanc/Noir OLED) et force la petite typographie.
class AppTheme {
// --- THÈME CLAIR (Mode Jour) ---
static final ThemeData lightTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.light,
primaryColor: AppColors.primaryGreen,
scaffoldBackgroundColor: AppColors.lightSurface,
colorScheme: const ColorScheme.light(
primary: AppColors.primaryGreen,
secondary: AppColors.brandGreenLight,
surface: AppColors.lightBackground,
error: AppColors.error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: AppColors.textPrimaryLight,
onError: Colors.white,
),
// Forcer la typographie standardisée
textTheme: const TextTheme(
titleMedium: AppTypography.headerSmall,
bodyMedium: AppTypography.bodyTextSmall,
bodySmall: AppTypography.subtitleSmall,
labelLarge: AppTypography.actionText,
labelSmall: AppTypography.badgeText,
),
// Personnalisation des AppBar (Garder la minimaliste)
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.lightBackground,
foregroundColor: AppColors.textPrimaryLight,
elevation: 0,
centerTitle: true,
iconTheme: IconThemeData(color: AppColors.textPrimaryLight, size: 20),
titleTextStyle: AppTypography.headerSmall,
),
// Boutons par défaut
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
elevation: 0,
textStyle: AppTypography.actionText,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
minimumSize: const Size(64, 32),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
// BottomNavigationBar ultra-compacte
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: AppColors.lightBackground,
selectedItemColor: AppColors.primaryGreen,
unselectedItemColor: AppColors.textSecondaryLight,
showSelectedLabels: false,
showUnselectedLabels: false,
elevation: 8,
type: BottomNavigationBarType.fixed,
),
dividerTheme: const DividerThemeData(
color: AppColors.lightBorder,
thickness: 1,
space: 1,
),
);
// --- THÈME SOMBRE (Mode Nuit OLED) ---
static final ThemeData darkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
primaryColor: AppColors.primaryGreen,
scaffoldBackgroundColor: AppColors.darkBackground, // Noir OLED
colorScheme: const ColorScheme.dark(
primary: AppColors.primaryGreen,
secondary: AppColors.brandGreenLight,
surface: AppColors.darkSurface, // Gris très sombre
error: AppColors.error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: AppColors.textPrimaryDark,
onError: Colors.white,
),
textTheme: const TextTheme(
titleMedium: AppTypography.headerSmall,
bodyMedium: AppTypography.bodyTextSmall,
bodySmall: AppTypography.subtitleSmall,
labelLarge: AppTypography.actionText,
labelSmall: AppTypography.badgeText,
).apply(
bodyColor: AppColors.textPrimaryDark,
displayColor: AppColors.textPrimaryDark,
),
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.darkBackground,
foregroundColor: AppColors.textPrimaryDark,
elevation: 0,
centerTitle: true,
iconTheme: IconThemeData(color: AppColors.textPrimaryDark, size: 20),
titleTextStyle: AppTypography.headerSmall, // Remplace titleTextStyle
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryGreen,
foregroundColor: Colors.white,
elevation: 0,
textStyle: AppTypography.actionText,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
minimumSize: const Size(64, 32),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: AppColors.darkBackground,
selectedItemColor: AppColors.primaryGreen,
unselectedItemColor: AppColors.textSecondaryDark,
showSelectedLabels: false,
showUnselectedLabels: false,
elevation: 8,
type: BottomNavigationBarType.fixed,
),
dividerTheme: const DividerThemeData(
color: AppColors.darkBorder,
thickness: 1,
space: 1,
),
);
}

View File

@@ -85,6 +85,22 @@ class AppThemeSophisticated {
);
}
/// Thème sombre (suit le système ou sélection manuelle)
static ThemeData get darkTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.dark(
primary: ColorTokens.primary,
onPrimary: Colors.white,
surface: const Color(0xFF121212),
onSurface: Colors.white,
error: ColorTokens.error,
),
scaffoldBackgroundColor: const Color(0xFF121212),
);
}
// ═══════════════════════════════════════════════════════════════════════════
// SCHÉMA DE COULEURS
// ═══════════════════════════════════════════════════════════════════════════
@@ -349,7 +365,7 @@ class AppThemeSophisticated {
contentTextStyle: TypographyTokens.bodyMedium,
);
/// Configuration des snackbars
/// Configuration des snackbars (fixed pour éviter "Floating SnackBar off screen" avec bottomNavigationBar)
static final SnackBarThemeData _snackBarTheme = SnackBarThemeData(
backgroundColor: ColorTokens.onSurface,
contentTextStyle: TypographyTokens.bodyMedium.copyWith(
@@ -358,7 +374,7 @@ class AppThemeSophisticated {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(SpacingTokens.radiusMd),
),
behavior: SnackBarBehavior.floating,
behavior: SnackBarBehavior.fixed,
);
/// Configuration des puces