'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) => (
),
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 });