refactor(admin): replace parameterized routes with modal-based editing for users and roles
This commit is contained in:
@@ -1,32 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Card, Table, Badge, StatusBadge, Button, UserAvatar } 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';
|
||||
|
||||
const UsersPageClient = () => {
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [roleColorMap, setRoleColorMap] = useState({});
|
||||
|
||||
// Pagination state
|
||||
const [editingUserId, setEditingUserId] = useState(null);
|
||||
|
||||
const [pagination, setPagination] = useState({
|
||||
page: 1,
|
||||
limit: 20,
|
||||
total: 0,
|
||||
totalPages: 0
|
||||
totalPages: 0,
|
||||
});
|
||||
|
||||
// Sort state
|
||||
const [sortBy, setSortBy] = useState('created_at');
|
||||
const [sortOrder, setSortOrder] = useState('desc');
|
||||
|
||||
// Table columns configuration
|
||||
const columns = [
|
||||
{
|
||||
key: 'name',
|
||||
@@ -43,8 +40,8 @@ const UsersPageClient = () => {
|
||||
),
|
||||
skeleton: {
|
||||
height: 'h-4', width: '60%',
|
||||
secondary: { height: 'h-3', width: '40%' }
|
||||
}
|
||||
secondary: { height: 'h-3', width: '40%' },
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'role',
|
||||
@@ -55,14 +52,14 @@ const UsersPageClient = () => {
|
||||
{user.role}
|
||||
</Badge>
|
||||
),
|
||||
skeleton: { height: 'h-6', width: '80px', className: 'rounded-full' }
|
||||
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' }
|
||||
skeleton: { height: 'h-6', width: '90px', className: 'rounded-full' },
|
||||
},
|
||||
{
|
||||
key: 'created_at',
|
||||
@@ -73,7 +70,7 @@ const UsersPageClient = () => {
|
||||
{formatDate(user.created_at)}
|
||||
</span>
|
||||
),
|
||||
skeleton: { height: 'h-4', width: '70%' }
|
||||
skeleton: { height: 'h-4', width: '70%' },
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
@@ -84,35 +81,28 @@ const UsersPageClient = () => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => router.push(`/admin/users/edit/${user.id}`)}
|
||||
onClick={() => setEditingUserId(user.id)}
|
||||
icon={<PencilEdit01Icon className="w-4 h-4" />}
|
||||
>
|
||||
Modifier
|
||||
</Button>
|
||||
),
|
||||
skeleton: { height: 'h-8', width: '80px', className: 'rounded-lg' }
|
||||
}
|
||||
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
|
||||
sortOrder,
|
||||
});
|
||||
|
||||
const response = await fetch(`/zen/api/users?${searchParams}`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error: ${response.status}`);
|
||||
}
|
||||
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);
|
||||
@@ -120,11 +110,10 @@ const UsersPageClient = () => {
|
||||
...prev,
|
||||
total: data.pagination.total,
|
||||
totalPages: data.pagination.totalPages,
|
||||
page: data.pagination.page
|
||||
page: data.pagination.page,
|
||||
}));
|
||||
} catch (err) {
|
||||
toast.error(err.message || 'Impossible de charger les utilisateurs');
|
||||
console.error('Error fetching users:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -143,32 +132,18 @@ const UsersPageClient = () => {
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
// 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 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);
|
||||
};
|
||||
|
||||
// Format date helper
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return 'N/A';
|
||||
try {
|
||||
@@ -177,7 +152,7 @@ const UsersPageClient = () => {
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
minute: '2-digit',
|
||||
});
|
||||
} catch {
|
||||
return 'Date invalide';
|
||||
@@ -185,8 +160,7 @@ const UsersPageClient = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Users Table */}
|
||||
<>
|
||||
<Card variant="default" padding="none">
|
||||
<Table
|
||||
columns={columns}
|
||||
@@ -205,17 +179,22 @@ const UsersPageClient = () => {
|
||||
total={pagination.total}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<UserEditModal
|
||||
userId={editingUserId}
|
||||
isOpen={!!editingUserId}
|
||||
onClose={() => setEditingUserId(null)}
|
||||
onSaved={fetchUsers}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const UsersPage = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
|
||||
<AdminHeader title="Utilisateurs" description="Gérez les comptes utilisateurs" />
|
||||
<UsersPageClient />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const UsersPage = () => (
|
||||
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
|
||||
<AdminHeader title="Utilisateurs" description="Gérez les comptes utilisateurs" />
|
||||
<UsersPageClient />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default UsersPage;
|
||||
|
||||
Reference in New Issue
Block a user