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:
@@ -9,6 +9,7 @@ import '../../data/models/organization_model.dart';
|
||||
import '../../bloc/organizations_bloc.dart';
|
||||
import '../../bloc/organizations_event.dart';
|
||||
import '../../bloc/organizations_state.dart';
|
||||
import 'edit_organization_page.dart';
|
||||
|
||||
/// Page de détail d'une organisation avec design system cohérent
|
||||
class OrganizationDetailPage extends StatefulWidget {
|
||||
@@ -387,7 +388,7 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
||||
child: _buildStatItem(
|
||||
icon: Icons.event,
|
||||
label: 'Événements',
|
||||
value: '0', // Nécessite endpoint stats par organisation
|
||||
value: (organization.nombreEvenements ?? 0).toString(),
|
||||
color: const Color(0xFF10B981),
|
||||
),
|
||||
),
|
||||
@@ -590,7 +591,7 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _showEditDialog(),
|
||||
onPressed: () => _showEditDialog(organization),
|
||||
icon: const Icon(Icons.edit),
|
||||
label: const Text('Modifier'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
@@ -725,11 +726,36 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Affiche le dialog d'édition
|
||||
void _showEditDialog() {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Édition - À implémenter')),
|
||||
);
|
||||
/// Ouvre la page d'édition ou le dialog selon le contexte
|
||||
void _showEditDialog([OrganizationModel? organization]) {
|
||||
if (organization == null) {
|
||||
final state = context.read<OrganizationsBloc>().state;
|
||||
if (state is OrganizationLoaded) {
|
||||
organization = state.organization;
|
||||
}
|
||||
}
|
||||
if (organization == null || !context.mounted) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Chargement de l\'organisation en cours...')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final org = organization;
|
||||
final bloc = context.read<OrganizationsBloc>();
|
||||
Navigator.of(context).push<void>(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: EditOrganizationPage(organization: org),
|
||||
),
|
||||
),
|
||||
).then((_) {
|
||||
if (context.mounted) {
|
||||
bloc.add(LoadOrganizationById(widget.organizationId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Affiche la confirmation de suppression
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,15 @@ library organisations_page_wrapper;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../di/organizations_di.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import '../../../authentication/data/models/user_role.dart';
|
||||
import '../../../authentication/presentation/bloc/auth_bloc.dart';
|
||||
import '../../bloc/organizations_bloc.dart';
|
||||
import '../../bloc/organizations_event.dart';
|
||||
import 'organizations_page.dart';
|
||||
|
||||
final _getIt = GetIt.instance;
|
||||
|
||||
/// Wrapper qui fournit le BLoC pour la page des organisations
|
||||
class OrganizationsPageWrapper extends StatelessWidget {
|
||||
const OrganizationsPageWrapper({super.key});
|
||||
@@ -14,7 +19,15 @@ class OrganizationsPageWrapper extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<OrganizationsBloc>(
|
||||
create: (context) => OrganizationsDI.getOrganizationsBloc(),
|
||||
create: (context) {
|
||||
final bloc = _getIt<OrganizationsBloc>();
|
||||
// Admin d'organisation : ne charger que son/ses organisation(s)
|
||||
final authState = context.read<AuthBloc>().state;
|
||||
final useMesOnly = authState is AuthAuthenticated &&
|
||||
authState.effectiveRole == UserRole.orgAdmin;
|
||||
bloc.add(LoadOrganizations(useMesOnly: useMesOnly));
|
||||
return bloc;
|
||||
},
|
||||
child: const OrganizationsPage(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user