admin/src/pages/UsersNew.tsx

255 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react';
import { Plus, Search, Filter } from 'lucide-react';
import { Table } from '../components/ui/Table';
import { Button } from '../components/ui/Button';
import { Modal } from '../components/ui/Modal';
import { Pagination } from '../components/ui/Pagination';
import { UserForm } from '../components/forms/UserForm';
import { PermissionWrapper } from '../components/common/PermissionWrapper';
import { TableColumn } from '../types';
import { UserFormData } from '../utils/validationSchemas';
import { formatDate } from '../utils/formatters';
import { useUsers, useCreateUser, useUpdateUser, useDeleteUser } from '../hooks/useUsers';
import { useFilters } from '../stores/useAppStore';
const Users = () => {
const [showUserModal, setShowUserModal] = useState(false);
const [editingUser, setEditingUser] = useState<any>(null);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 5;
const { filters, setFilters } = useFilters();
const queryParams = {
page: currentPage,
limit: itemsPerPage,
search: filters.search,
sortBy: 'createdAt',
sortOrder: 'desc' as const,
};
const { data: usersResponse, isLoading, error } = useUsers(queryParams);
const createUserMutation = useCreateUser();
const updateUserMutation = useUpdateUser();
const deleteUserMutation = useDeleteUser();
const users = usersResponse?.data || [];
const totalItems = usersResponse?.total || 0;
const totalPages = Math.ceil(totalItems / itemsPerPage);
const columns: TableColumn[] = [
{ key: 'name', label: 'نام', sortable: true },
{ key: 'email', label: 'ایمیل', sortable: true },
{ key: 'phone', label: 'تلفن' },
{ key: 'role', label: 'نقش' },
{
key: 'status',
label: 'وضعیت',
render: (value) => (
<span className={`px-2 py-1 rounded-full text-xs font-medium ${value === 'active'
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
}`}>
{value === 'active' ? 'فعال' : 'غیرفعال'}
</span>
)
},
{
key: 'createdAt',
label: 'تاریخ عضویت',
sortable: true,
render: (value) => formatDate(value)
},
{
key: 'actions',
label: 'عملیات',
render: (_, row) => (
<div className="flex space-x-2">
<Button
size="sm"
variant="secondary"
onClick={() => handleEditUser(row)}
disabled={updateUserMutation.isPending}
>
ویرایش
</Button>
<PermissionWrapper permission={22}>
<Button
size="sm"
variant="danger"
onClick={() => handleDeleteUser(row.id)}
disabled={deleteUserMutation.isPending}
>
حذف
</Button>
</PermissionWrapper>
</div>
)
}
];
const handleAddUser = () => {
setEditingUser(null);
setShowUserModal(true);
};
const handleEditUser = (user: any) => {
setEditingUser(user);
setShowUserModal(true);
};
const handleDeleteUser = async (userId: string) => {
if (confirm('آیا از حذف این کاربر اطمینان دارید؟')) {
try {
await deleteUserMutation.mutateAsync(userId);
} catch (error) {
console.error('Delete error:', error);
}
}
};
const handleSubmitUser = async (data: UserFormData) => {
try {
if (editingUser) {
await updateUserMutation.mutateAsync({
id: editingUser.id,
data: {
name: data.name,
email: data.email,
phone: data.phone,
role: data.role,
}
});
} else {
await createUserMutation.mutateAsync({
name: data.name,
email: data.email,
phone: data.phone,
role: data.role,
password: data.password || '123456',
});
}
setShowUserModal(false);
} catch (error) {
console.error('Submit error:', error);
}
};
const handleCloseModal = () => {
setShowUserModal(false);
setEditingUser(null);
};
const handleSearchChange = (value: string) => {
setFilters({ search: value });
setCurrentPage(1);
};
if (error) {
return (
<div className="p-6">
<div className="text-center py-12">
<p className="text-red-600 dark:text-red-400">
خطا در بارگذاری کاربران: {error.message}
</p>
<Button
onClick={() => window.location.reload()}
className="mt-4"
>
تلاش دوباره
</Button>
</div>
</div>
);
}
return (
<div className="p-6 space-y-6">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div>
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
مدیریت کاربران
</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
{totalItems} کاربر یافت شد
</p>
</div>
<div className="flex items-center space-x-3 space-x-reverse">
<Button variant="secondary">
<Filter className="h-4 w-4 ml-2" />
فیلتر
</Button>
<PermissionWrapper permission={25}>
<button
onClick={handleAddUser}
disabled={createUserMutation.isPending}
className="flex items-center justify-center w-12 h-12 bg-primary-600 hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-full transition-colors duration-200 text-white shadow-lg hover:shadow-xl"
title="افزودن کاربر"
>
<Plus className="h-5 w-5" />
</button>
</PermissionWrapper>
</div>
</div>
<div className="card p-6">
<div className="mb-6">
<div className="relative">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<Search className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
placeholder="جستجو در کاربران..."
value={filters.search}
onChange={(e) => handleSearchChange(e.target.value)}
className="input pr-10 max-w-md"
/>
</div>
</div>
{isLoading ? (
<div className="bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
<Table columns={columns} data={[]} loading={true} />
</div>
) : (
<>
<div className="bg-white dark:bg-gray-800 rounded-lg overflow-hidden">
<Table
columns={columns}
data={users}
loading={isLoading}
/>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
itemsPerPage={itemsPerPage}
totalItems={totalItems}
/>
</div>
</>
)}
</div>
<Modal
title={editingUser ? "ویرایش کاربر" : "افزودن کاربر"}
isOpen={showUserModal}
onClose={handleCloseModal}
size="lg"
>
<UserForm
initialData={editingUser}
onSubmit={handleSubmitUser}
onCancel={handleCloseModal}
loading={createUserMutation.isPending || updateUserMutation.isPending}
isEdit={!!editingUser}
/>
</Modal>
</div>
);
};
export default Users;