Files
core/src/features/admin/pages/UsersPage.client.js
T

217 lines
6.9 KiB
JavaScript

'use client';
import React, { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Card, Table, StatusBadge, Button } from '@zen/core/shared/components';
import { PencilEdit01Icon } from '@zen/core/shared/icons';
import { useToast } from '@zen/core/toast';
const UsersPageClient = () => {
const router = useRouter();
const toast = useToast();
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
// Pagination state
const [pagination, setPagination] = useState({
page: 1,
limit: 20,
total: 0,
totalPages: 0
});
// Sort state
const [sortBy, setSortBy] = useState('created_at');
const [sortOrder, setSortOrder] = useState('desc');
// Table columns configuration
const columns = [
{
key: 'name',
label: 'Nom',
sortable: true,
render: (user) => (
<div>
<div className="text-sm font-medium text-neutral-900 dark:text-white">{user.name}</div>
<div className="text-xs text-neutral-500 dark:text-gray-400">ID: {user.id.slice(0, 8)}...</div>
</div>
),
skeleton: {
height: 'h-4', width: '60%',
secondary: { height: 'h-3', width: '40%' }
}
},
{
key: 'email',
label: 'Email',
sortable: true,
render: (user) => <div className="text-sm font-medium text-neutral-900 dark:text-white">{user.email}</div>,
skeleton: {
height: 'h-4',
width: '60%',
}
},
{
key: 'role',
label: 'Rôle',
sortable: true,
render: (user) => <StatusBadge status={user.role} />,
skeleton: { height: 'h-6', width: '80px', className: 'rounded-full' }
},
{
key: 'email_verified',
label: 'Statut',
sortable: true,
render: (user) => <StatusBadge status={user.email_verified ? 'verified' : 'unverified'} />,
skeleton: { height: 'h-6', width: '90px', className: 'rounded-full' }
},
{
key: 'created_at',
label: 'Créé le',
sortable: true,
render: (user) => (
<span className="text-sm text-neutral-600 dark:text-gray-300">
{formatDate(user.created_at)}
</span>
),
skeleton: { height: 'h-4', width: '70%' }
},
{
key: 'actions',
label: '',
sortable: false,
noWrap: true,
render: (user) => (
<Button
variant="secondary"
size="sm"
onClick={() => router.push(`/admin/users/edit/${user.id}`)}
icon={<PencilEdit01Icon className="w-4 h-4" />}
>
Modifier
</Button>
),
skeleton: { height: 'h-8', width: '80px', className: 'rounded-lg' }
}
];
// Fetch users function
const fetchUsers = async () => {
setLoading(true);
try {
const searchParams = new URLSearchParams({
page: pagination.page.toString(),
limit: pagination.limit.toString(),
sortBy,
sortOrder
});
const response = await fetch(`/zen/api/users?${searchParams}`, {
credentials: 'include'
});
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
const data = await response.json();
setUsers(data.users);
setPagination(prev => ({
...prev,
total: data.pagination.total,
totalPages: data.pagination.totalPages,
page: data.pagination.page
}));
} catch (err) {
toast.error(err.message || 'Impossible de charger les utilisateurs');
console.error('Error fetching users:', err);
} finally {
setLoading(false);
}
};
// Effect to fetch users when sort or pagination change
useEffect(() => {
fetchUsers();
}, [sortBy, sortOrder, pagination.page, pagination.limit]);
// Handle pagination
const handlePageChange = (newPage) => {
setPagination(prev => ({ ...prev, page: newPage }));
};
const handleLimitChange = (newLimit) => {
setPagination(prev => ({
...prev,
limit: newLimit,
page: 1 // Reset to first page when changing limit
}));
};
// Handle sorting
const handleSort = (newSortBy) => {
const newSortOrder = sortBy === newSortBy && sortOrder === 'asc' ? 'desc' : 'asc';
setSortBy(newSortBy);
setSortOrder(newSortOrder);
};
// Format date helper
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
try {
return new Date(dateString).toLocaleDateString('fr-FR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch {
return 'Date invalide';
}
};
return (
<div className="flex flex-col gap-6">
{/* Users Table */}
<Card variant="default" padding="none">
<Table
columns={columns}
data={users}
loading={loading}
sortBy={sortBy}
sortOrder={sortOrder}
onSort={handleSort}
emptyMessage="Aucun utilisateur trouvé"
emptyDescription="La base de données est vide"
currentPage={pagination.page}
totalPages={pagination.totalPages}
onPageChange={handlePageChange}
onLimitChange={handleLimitChange}
limit={pagination.limit}
total={pagination.total}
/>
</Card>
</div>
);
};
const UsersPage = () => {
return (
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-lg sm:text-xl font-semibold text-neutral-900 dark:text-white">Utilisateurs</h1>
<p className="mt-1 text-[13px] text-neutral-500 dark:text-neutral-400">
Gérez les comptes utilisateurs
</p>
</div>
</div>
<UsersPageClient />
</div>
);
};
export default UsersPage;