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:
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user