fix(UI-02): guards rôle sur pages organisations — FAB, édition, suppression SUPER_ADMIN only
- 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é
This commit is contained in:
@@ -5,6 +5,8 @@ import '../../../../shared/widgets/core_card.dart';
|
|||||||
import '../../../../core/di/injection.dart';
|
import '../../../../core/di/injection.dart';
|
||||||
import '../../bloc/org_types_bloc.dart';
|
import '../../bloc/org_types_bloc.dart';
|
||||||
import '../../domain/entities/type_reference_entity.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 {
|
class OrgTypesPage extends StatelessWidget {
|
||||||
const OrgTypesPage({super.key});
|
const OrgTypesPage({super.key});
|
||||||
@@ -77,11 +79,17 @@ class _OrgTypesViewState extends State<_OrgTypesView> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton.small(
|
floatingActionButton: Builder(builder: (ctx) {
|
||||||
onPressed: () => _showTypeForm(context, null),
|
final authState = ctx.read<AuthBloc>().state;
|
||||||
backgroundColor: AppColors.primaryGreen,
|
final isSuperAdmin = authState is AuthAuthenticated &&
|
||||||
child: const Icon(Icons.add, color: Colors.white),
|
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) {
|
Widget _buildTypeCard(BuildContext context, TypeReferenceEntity type, OrgTypesState state) {
|
||||||
final isOperating = state is OrgTypeOperating;
|
final isOperating = state is OrgTypeOperating;
|
||||||
final color = _parseColor(type.couleur) ?? AppColors.primaryGreen;
|
final color = _parseColor(type.couleur) ?? AppColors.primaryGreen;
|
||||||
|
final authState = context.read<AuthBloc>().state;
|
||||||
|
final isSuperAdmin = authState is AuthAuthenticated &&
|
||||||
|
authState.effectiveRole == UserRole.superAdmin;
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: isOperating ? 0.6 : 1.0,
|
opacity: isOperating ? 0.6 : 1.0,
|
||||||
child: CoreCard(
|
child: CoreCard(
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
onTap: (!type.estSysteme && !isOperating) ? () => _showTypeForm(context, type) : null,
|
onTap: (isSuperAdmin && !type.estSysteme && !isOperating) ? () => _showTypeForm(context, type) : null,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(left: BorderSide(color: color, width: 3)),
|
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(
|
IconButton(
|
||||||
icon: const Icon(Icons.edit_outlined, size: 16),
|
icon: const Icon(Icons.edit_outlined, size: 16),
|
||||||
color: AppColors.textSecondaryLight,
|
color: AppColors.textSecondaryLight,
|
||||||
@@ -187,6 +198,10 @@ class _OrgTypesViewState extends State<_OrgTypesView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyState(BuildContext context) {
|
Widget _buildEmptyState(BuildContext context) {
|
||||||
|
final authState = context.read<AuthBloc>().state;
|
||||||
|
final isSuperAdmin = authState is AuthAuthenticated &&
|
||||||
|
authState.effectiveRole == UserRole.superAdmin;
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(24),
|
||||||
@@ -201,22 +216,26 @@ class _OrgTypesViewState extends State<_OrgTypesView> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
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]),
|
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
if (isSuperAdmin) ...[
|
||||||
ElevatedButton.icon(
|
const SizedBox(height: 16),
|
||||||
onPressed: () => _showTypeForm(context, null),
|
ElevatedButton.icon(
|
||||||
icon: const Icon(Icons.add, size: 16),
|
onPressed: () => _showTypeForm(context, null),
|
||||||
label: const Text('Créer un type'),
|
icon: const Icon(Icons.add, size: 16),
|
||||||
style: ElevatedButton.styleFrom(
|
label: const Text('Créer un type'),
|
||||||
backgroundColor: AppColors.primaryGreen,
|
style: ElevatedButton.styleFrom(
|
||||||
foregroundColor: Colors.white,
|
backgroundColor: AppColors.primaryGreen,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
foregroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -51,7 +51,14 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
|||||||
title: const Text('Détail Organisation'),
|
title: const Text('Détail Organisation'),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(onPressed: _showEditPage, icon: const Icon(Icons.edit), tooltip: 'Modifier'),
|
Builder(builder: (ctx) {
|
||||||
|
final authState = ctx.read<AuthBloc>().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) {
|
Builder(builder: (ctx) {
|
||||||
final authState = ctx.read<AuthBloc>().state;
|
final authState = ctx.read<AuthBloc>().state;
|
||||||
final isSuperAdmin = authState is AuthAuthenticated &&
|
final isSuperAdmin = authState is AuthAuthenticated &&
|
||||||
@@ -314,6 +321,16 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
|||||||
// ── Actions ─────────────────────────────────────────────────────────────────
|
// ── Actions ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
Widget _buildActionsCard(OrganizationModel org) {
|
Widget _buildActionsCard(OrganizationModel org) {
|
||||||
|
final authState = context.read<AuthBloc>().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, [
|
return _buildCard('Actions', Icons.bolt, [
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Expanded(child: ElevatedButton.icon(
|
Expanded(child: ElevatedButton.icon(
|
||||||
@@ -322,13 +339,15 @@ class _OrganizationDetailPageState extends State<OrganizationDetailPage> {
|
|||||||
label: const Text('Modifier'),
|
label: const Text('Modifier'),
|
||||||
style: ElevatedButton.styleFrom(backgroundColor: AppColors.primaryGreen, foregroundColor: Colors.white),
|
style: ElevatedButton.styleFrom(backgroundColor: AppColors.primaryGreen, foregroundColor: Colors.white),
|
||||||
)),
|
)),
|
||||||
const SizedBox(width: 12),
|
if (isSuperAdmin) ...[
|
||||||
Expanded(child: OutlinedButton.icon(
|
const SizedBox(width: 12),
|
||||||
onPressed: () => _showDeleteConfirmation(org),
|
Expanded(child: OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.delete),
|
onPressed: () => _showDeleteConfirmation(org),
|
||||||
label: const Text('Supprimer'),
|
icon: const Icon(Icons.delete),
|
||||||
style: OutlinedButton.styleFrom(foregroundColor: Colors.red, side: const BorderSide(color: Colors.red)),
|
label: const Text('Supprimer'),
|
||||||
)),
|
style: OutlinedButton.styleFrom(foregroundColor: Colors.red, side: const BorderSide(color: Colors.red)),
|
||||||
|
)),
|
||||||
|
],
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user