refactor(#93): homogénéiser la présentation des onglets admin

Uniformise les 4 onglets de gestion admin avec des composants UI partagés (header, états de liste, carte utilisateur) pour garantir une expérience cohérente sans changement backend.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-23 17:31:50 +01:00
co-authored by Cursor
parent fbafef8f2c
commit b2d6414fab
7 changed files with 334 additions and 310 deletions
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
class AdminListHeader extends StatelessWidget {
final TextEditingController searchController;
final String searchHint;
final String? actionLabel;
final VoidCallback? onActionPressed;
final Widget? filters;
const AdminListHeader({
super.key,
required this.searchController,
required this.searchHint,
this.actionLabel,
this.onActionPressed,
this.filters,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: TextField(
controller: searchController,
decoration: InputDecoration(
hintText: searchHint,
prefixIcon: const Icon(Icons.search),
border: const OutlineInputBorder(),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 12,
),
),
),
),
if (actionLabel != null && onActionPressed != null) ...[
const SizedBox(width: 16),
ElevatedButton.icon(
onPressed: onActionPressed,
icon: const Icon(Icons.add),
label: Text(actionLabel!),
),
],
],
),
if (filters != null) ...[
const SizedBox(height: 12),
filters!,
],
],
);
}
}
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
class AdminListState extends StatelessWidget {
final bool isLoading;
final String? error;
final bool isEmpty;
final String emptyMessage;
final Widget list;
const AdminListState({
super.key,
required this.isLoading,
required this.error,
required this.isEmpty,
required this.emptyMessage,
required this.list,
});
@override
Widget build(BuildContext context) {
if (isLoading) {
return const Expanded(
child: Center(child: CircularProgressIndicator()),
);
}
if (error != null) {
return Expanded(
child: Center(
child: Text(
'Erreur: $error',
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
);
}
if (isEmpty) {
return Expanded(
child: Center(
child: Text(emptyMessage),
),
);
}
return Expanded(child: list);
}
}
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
class AdminUserCard extends StatelessWidget {
final String title;
final List<String> subtitleLines;
final String? avatarUrl;
final IconData fallbackIcon;
final List<Widget> actions;
const AdminUserCard({
super.key,
required this.title,
required this.subtitleLines,
this.avatarUrl,
this.fallbackIcon = Icons.person,
this.actions = const [],
});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: ListTile(
leading: CircleAvatar(
backgroundImage: avatarUrl != null ? NetworkImage(avatarUrl!) : null,
child: avatarUrl == null ? Icon(fallbackIcon) : null,
),
title: Text(title.isNotEmpty ? title : 'Sans nom'),
subtitle: Text(subtitleLines.join('\n')),
isThreeLine: subtitleLines.length > 1,
trailing: actions.isEmpty
? null
: Row(
mainAxisSize: MainAxisSize.min,
children: actions,
),
),
);
}
}