'use client'; import { registerPage } from '../registry.js'; import { useState, useEffect } from 'react'; import { Card, Table, Badge, StatusBadge, Button, UserAvatar, RelativeDate, RoleBadge } from '@zen/core/shared/components'; import { PencilEdit01Icon } from '@zen/core/shared/icons'; import { useToast } from '@zen/core/toast'; import AdminHeader from '../components/AdminHeader.js'; import UserEditModal from '../components/UserEditModal.client.js'; import UserCreateModal from '../components/UserCreateModal.client.js'; const UsersPageClient = ({ currentUserId, refreshKey, canEdit }) => { const toast = useToast(); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [editingUserId, setEditingUserId] = useState(null); const [pagination, setPagination] = useState({ page: 1, limit: 20, total: 0, totalPages: 0, }); const [sortBy, setSortBy] = useState('created_at'); const [sortOrder, setSortOrder] = useState('desc'); const columns = [ { key: 'name', label: 'Utilisateur', sortable: true, render: (user) => (
{user.name}
{user.email}
), skeleton: { height: 'h-4', width: '60%', secondary: { height: 'h-3', width: '40%' }, }, }, { key: 'role', label: 'Rôle', sortable: true, render: (user) => { const roles = user.roles || []; const visible = roles.slice(0, 3); const overflow = roles.length - 3; return (
{visible.map(role => ( ))} {overflow > 0 && +{overflow}} {roles.length === 0 && }
); }, skeleton: { height: 'h-6', width: '140px', className: 'rounded-full' }, }, { key: 'email_verified', label: 'Statut', sortable: true, render: (user) => , skeleton: { height: 'h-6', width: '90px', className: 'rounded-full' }, }, { key: 'created_at', label: 'Créé le', sortable: true, render: (user) => , skeleton: { height: 'h-4', width: '70%' }, }, ...(canEdit ? [{ key: 'actions', label: '', sortable: false, noWrap: true, align: 'right', render: (user) => ( ), skeleton: { height: 'h-8', width: '80px', className: 'rounded-lg' }, }] : []), ]; 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'); } finally { setLoading(false); } }; useEffect(() => { fetchUsers(); }, [sortBy, sortOrder, pagination.page, pagination.limit, refreshKey]); const handlePageChange = (newPage) => setPagination(prev => ({ ...prev, page: newPage })); const handleLimitChange = (newLimit) => setPagination(prev => ({ ...prev, limit: newLimit, page: 1 })); const handleSort = (newSortBy) => { const newSortOrder = sortBy === newSortBy && sortOrder === 'asc' ? 'desc' : 'asc'; setSortBy(newSortBy); setSortOrder(newSortOrder); }; return ( <> {canEdit && ( setEditingUserId(null)} onSaved={fetchUsers} /> )} ); }; const UsersPage = ({ user }) => { const [createModalOpen, setCreateModalOpen] = useState(false); const [refreshKey, setRefreshKey] = useState(0); const canEdit = user?.permissions?.includes('users.manage'); return (
setCreateModalOpen(true)}> Nouvel utilisateur )} /> {canEdit && ( setCreateModalOpen(false)} onSaved={() => setRefreshKey(k => k + 1)} /> )}
); }; export default UsersPage; registerPage({ slug: 'users', title: 'Utilisateurs', Component: UsersPage });