From cf2866a0aa60756afa742af80a6b17768eb24dec Mon Sep 17 00:00:00 2001 From: dahoud <41957584+DahoudG@users.noreply.github.com> Date: Sat, 11 Apr 2026 01:27:42 +0000 Subject: [PATCH] =?UTF-8?q?fix(UI-02):=20guards=20r=C3=B4le=20sur=20pages?= =?UTF-8?q?=20organisations=20=E2=80=94=20FAB,=20=C3=A9dition,=20suppressi?= =?UTF-8?q?on=20SUPER=5FADMIN=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OrgTypesPage: FAB création, tap/édition/suppression type visibles uniquement SUPER_ADMIN - OrganizationDetailPage: bouton édition visible orgAdmin+superAdmin, suppression SUPER_ADMIN only, section Actions cachée si non autorisé --- .../presentation/pages/org_types_page.dart | 57 ++++++++++++------- .../pages/organization_detail_page.dart | 35 +++++++++--- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/lib/features/organizations/presentation/pages/org_types_page.dart b/lib/features/organizations/presentation/pages/org_types_page.dart index fca04dc..fcf589a 100644 --- a/lib/features/organizations/presentation/pages/org_types_page.dart +++ b/lib/features/organizations/presentation/pages/org_types_page.dart @@ -5,6 +5,8 @@ import '../../../../shared/widgets/core_card.dart'; import '../../../../core/di/injection.dart'; import '../../bloc/org_types_bloc.dart'; import '../../domain/entities/type_reference_entity.dart'; +import '../../../../features/authentication/presentation/bloc/auth_bloc.dart'; +import '../../../../features/authentication/data/models/user_role.dart'; class OrgTypesPage extends StatelessWidget { const OrgTypesPage({super.key}); @@ -77,11 +79,17 @@ class _OrgTypesViewState extends State<_OrgTypesView> { ); }, ), - floatingActionButton: FloatingActionButton.small( - onPressed: () => _showTypeForm(context, null), - backgroundColor: AppColors.primaryGreen, - child: const Icon(Icons.add, color: Colors.white), - ), + floatingActionButton: Builder(builder: (ctx) { + final authState = ctx.read().state; + final isSuperAdmin = authState is AuthAuthenticated && + authState.effectiveRole == UserRole.superAdmin; + if (!isSuperAdmin) return const SizedBox.shrink(); + return FloatingActionButton.small( + onPressed: () => _showTypeForm(context, null), + backgroundColor: AppColors.primaryGreen, + child: const Icon(Icons.add, color: Colors.white), + ); + }), ); } @@ -96,12 +104,15 @@ class _OrgTypesViewState extends State<_OrgTypesView> { Widget _buildTypeCard(BuildContext context, TypeReferenceEntity type, OrgTypesState state) { final isOperating = state is OrgTypeOperating; final color = _parseColor(type.couleur) ?? AppColors.primaryGreen; + final authState = context.read().state; + final isSuperAdmin = authState is AuthAuthenticated && + authState.effectiveRole == UserRole.superAdmin; return Opacity( opacity: isOperating ? 0.6 : 1.0, child: CoreCard( margin: EdgeInsets.zero, - onTap: (!type.estSysteme && !isOperating) ? () => _showTypeForm(context, type) : null, + onTap: (isSuperAdmin && !type.estSysteme && !isOperating) ? () => _showTypeForm(context, type) : null, child: Container( decoration: BoxDecoration( border: Border(left: BorderSide(color: color, width: 3)), @@ -163,7 +174,7 @@ class _OrgTypesViewState extends State<_OrgTypesView> { ], ), ), - if (!type.estSysteme && !isOperating) ...[ + if (isSuperAdmin && !type.estSysteme && !isOperating) ...[ IconButton( icon: const Icon(Icons.edit_outlined, size: 16), color: AppColors.textSecondaryLight, @@ -187,6 +198,10 @@ class _OrgTypesViewState extends State<_OrgTypesView> { } Widget _buildEmptyState(BuildContext context) { + final authState = context.read().state; + final isSuperAdmin = authState is AuthAuthenticated && + authState.effectiveRole == UserRole.superAdmin; + return Center( child: Padding( padding: const EdgeInsets.all(24), @@ -201,22 +216,26 @@ class _OrgTypesViewState extends State<_OrgTypesView> { ), const SizedBox(height: 6), Text( - 'Créez votre premier type d\'organisation', + isSuperAdmin + ? 'Créez votre premier type d\'organisation' + : 'Aucun type d\'organisation disponible', style: TextStyle(fontSize: 12, color: Colors.grey[500]), textAlign: TextAlign.center, ), - const SizedBox(height: 16), - ElevatedButton.icon( - onPressed: () => _showTypeForm(context, null), - icon: const Icon(Icons.add, size: 16), - label: const Text('Créer un type'), - style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryGreen, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + if (isSuperAdmin) ...[ + const SizedBox(height: 16), + ElevatedButton.icon( + onPressed: () => _showTypeForm(context, null), + icon: const Icon(Icons.add, size: 16), + label: const Text('Créer un type'), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primaryGreen, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), ), - ), + ], ], ), ), diff --git a/lib/features/organizations/presentation/pages/organization_detail_page.dart b/lib/features/organizations/presentation/pages/organization_detail_page.dart index d7d7aa9..7b19b32 100644 --- a/lib/features/organizations/presentation/pages/organization_detail_page.dart +++ b/lib/features/organizations/presentation/pages/organization_detail_page.dart @@ -51,7 +51,14 @@ class _OrganizationDetailPageState extends State { title: const Text('Détail Organisation'), elevation: 0, actions: [ - IconButton(onPressed: _showEditPage, icon: const Icon(Icons.edit), tooltip: 'Modifier'), + Builder(builder: (ctx) { + final authState = ctx.read().state; + final canEdit = authState is AuthAuthenticated && + (authState.effectiveRole == UserRole.superAdmin || + authState.effectiveRole == UserRole.orgAdmin); + if (!canEdit) return const SizedBox.shrink(); + return IconButton(onPressed: _showEditPage, icon: const Icon(Icons.edit), tooltip: 'Modifier'); + }), Builder(builder: (ctx) { final authState = ctx.read().state; final isSuperAdmin = authState is AuthAuthenticated && @@ -314,6 +321,16 @@ class _OrganizationDetailPageState extends State { // ── Actions ───────────────────────────────────────────────────────────────── Widget _buildActionsCard(OrganizationModel org) { + final authState = context.read().state; + final isSuperAdmin = authState is AuthAuthenticated && + authState.effectiveRole == UserRole.superAdmin; + final canEdit = authState is AuthAuthenticated && + (authState.effectiveRole == UserRole.superAdmin || + authState.effectiveRole == UserRole.orgAdmin); + + // Aucune action autorisée → ne pas afficher la section + if (!canEdit) return const SizedBox.shrink(); + return _buildCard('Actions', Icons.bolt, [ Row(children: [ Expanded(child: ElevatedButton.icon( @@ -322,13 +339,15 @@ class _OrganizationDetailPageState extends State { label: const Text('Modifier'), style: ElevatedButton.styleFrom(backgroundColor: AppColors.primaryGreen, foregroundColor: Colors.white), )), - const SizedBox(width: 12), - Expanded(child: OutlinedButton.icon( - onPressed: () => _showDeleteConfirmation(org), - icon: const Icon(Icons.delete), - label: const Text('Supprimer'), - style: OutlinedButton.styleFrom(foregroundColor: Colors.red, side: const BorderSide(color: Colors.red)), - )), + if (isSuperAdmin) ...[ + const SizedBox(width: 12), + Expanded(child: OutlinedButton.icon( + onPressed: () => _showDeleteConfirmation(org), + icon: const Icon(Icons.delete), + label: const Text('Supprimer'), + style: OutlinedButton.styleFrom(foregroundColor: Colors.red, side: const BorderSide(color: Colors.red)), + )), + ], ]), ]); }