Refactor UserManagement component layout: Update user display to a grid format for improved responsiveness, enhance user information presentation with clearer stats and action buttons, and streamline the overall UI for better user experience.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m3s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m3s
This commit is contained in:
@@ -196,108 +196,117 @@ export default function UserManagement() {
|
||||
Aucun utilisateur trouvé
|
||||
</div>
|
||||
) : (
|
||||
users.map((user) => {
|
||||
return (
|
||||
<Card key={user.id} variant="default" className="p-3 sm:p-4">
|
||||
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-2">
|
||||
<div className="flex gap-2 sm:gap-3 items-center flex-1 min-w-0">
|
||||
{/* Avatar */}
|
||||
<Avatar
|
||||
src={user.avatar}
|
||||
username={user.username}
|
||||
size="sm"
|
||||
className="flex-shrink-0"
|
||||
borderClassName="border-2 border-pixel-gold/50"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 flex-wrap">
|
||||
<h3 className="text-pixel-gold font-bold text-sm sm:text-base break-words">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4">
|
||||
{users.map((user) => {
|
||||
return (
|
||||
<Card key={user.id} variant="default" className="p-3">
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Header avec avatar et nom */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar
|
||||
src={user.avatar}
|
||||
username={user.username}
|
||||
size="sm"
|
||||
className="flex-shrink-0"
|
||||
borderClassName="border-2 border-pixel-gold/50"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-pixel-gold font-bold text-sm truncate">
|
||||
{user.username}
|
||||
</h3>
|
||||
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
|
||||
Niveau {user.level}
|
||||
</span>
|
||||
<span className="text-[10px] sm:text-xs text-gray-500 whitespace-nowrap">
|
||||
Score: {formatNumber(user.score)}
|
||||
</span>
|
||||
<span
|
||||
className={`text-[10px] sm:text-xs whitespace-nowrap ${
|
||||
user.role === "ADMIN"
|
||||
? "text-pixel-gold"
|
||||
: "text-gray-500"
|
||||
}`}
|
||||
>
|
||||
{user.role}
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5 mt-0.5">
|
||||
<span className="text-[10px] text-gray-500">
|
||||
Niv. {user.level}
|
||||
</span>
|
||||
<span
|
||||
className={`text-[10px] font-bold px-1.5 py-0.5 rounded border ${
|
||||
user.role === "ADMIN"
|
||||
? "text-pixel-gold border-pixel-gold/50 bg-pixel-gold/10"
|
||||
: "text-gray-500 border-gray-500/30 bg-gray-500/10"
|
||||
}`}
|
||||
>
|
||||
{user.role}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-400 text-[10px] sm:text-xs truncate">
|
||||
{user.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-shrink-0 sm:ml-2">
|
||||
<button
|
||||
onClick={() => handleEdit(user)}
|
||||
className="px-2 sm:px-3 py-1.5 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-pixel-gold/10 transition whitespace-nowrap"
|
||||
>
|
||||
Modifier
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(user.id)}
|
||||
disabled={deletingUserId === user.id}
|
||||
className="px-2 sm:px-3 py-1.5 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] sm:text-xs tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50 whitespace-nowrap"
|
||||
>
|
||||
{deletingUserId === user.id
|
||||
? "Suppression..."
|
||||
: "Supprimer"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Affichage des stats */}
|
||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 text-[10px] sm:text-xs">
|
||||
<div className="flex-1">
|
||||
<div className="flex justify-between items-center mb-0.5">
|
||||
<span className="text-gray-400">HP</span>
|
||||
<span className="text-gray-400">
|
||||
{user.hp}/{user.maxHp}
|
||||
{/* Score en évidence */}
|
||||
<div className="flex items-baseline gap-1.5 px-1">
|
||||
<span className="text-[10px] text-gray-400">Score:</span>
|
||||
<span className="text-lg font-bold text-pixel-gold">
|
||||
{formatNumber(user.score)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1.5 bg-black/60 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-red-600 to-green-500"
|
||||
style={{
|
||||
width: `${Math.min(
|
||||
100,
|
||||
(user.hp / user.maxHp) * 100
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Stats HP et XP */}
|
||||
<div className="space-y-1.5 text-[10px]">
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-0.5">
|
||||
<span className="text-gray-400">HP</span>
|
||||
<span className="text-gray-400">
|
||||
{user.hp}/{user.maxHp}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1 bg-black/60 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-red-600 to-green-500"
|
||||
style={{
|
||||
width: `${Math.min(
|
||||
100,
|
||||
(user.hp / user.maxHp) * 100
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-0.5">
|
||||
<span className="text-gray-400">XP</span>
|
||||
<span className="text-gray-400">
|
||||
{formatNumber(user.xp)}/{formatNumber(user.maxXp)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1 bg-black/60 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-blue-600 to-purple-500"
|
||||
style={{
|
||||
width: `${Math.min(
|
||||
100,
|
||||
(user.xp / user.maxXp) * 100
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<p className="text-gray-400 text-[10px] truncate px-1">
|
||||
{user.email}
|
||||
</p>
|
||||
|
||||
{/* Boutons d'action */}
|
||||
<div className="flex gap-2 pt-1">
|
||||
<button
|
||||
onClick={() => handleEdit(user)}
|
||||
className="flex-1 px-2 py-1 border border-pixel-gold/50 bg-black/60 text-white uppercase text-[10px] tracking-widest rounded hover:bg-pixel-gold/10 transition"
|
||||
>
|
||||
Modifier
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(user.id)}
|
||||
disabled={deletingUserId === user.id}
|
||||
className="flex-1 px-2 py-1 border border-red-500/50 bg-red-900/20 text-red-400 uppercase text-[10px] tracking-widest rounded hover:bg-red-900/30 transition disabled:opacity-50"
|
||||
>
|
||||
{deletingUserId === user.id ? "..." : "Suppr."}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex justify-between items-center mb-0.5">
|
||||
<span className="text-gray-400">XP</span>
|
||||
<span className="text-gray-400">
|
||||
{formatNumber(user.xp)}/{formatNumber(user.maxXp)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1.5 bg-black/60 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-blue-600 to-purple-500"
|
||||
style={{
|
||||
width: `${Math.min(
|
||||
100,
|
||||
(user.xp / user.maxXp) * 100
|
||||
)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
})
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modal d'édition */}
|
||||
|
||||
Reference in New Issue
Block a user