133 lines
6.1 KiB
TypeScript
133 lines
6.1 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import { Search, Trash2, Heart, Image as ImageIcon } from 'lucide-react';
|
||
import api, { BASE_DOMAIN } from '../services/api';
|
||
import Spinner from '../components/ui/Spinner';
|
||
import Pagination from '../components/ui/Pagination';
|
||
|
||
export default function Pets() {
|
||
const [pets, setPets] = useState<any[]>([]);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [search, setSearch] = useState('');
|
||
const [page, setPage] = useState(1);
|
||
const [totalPages, setTotalPages] = useState(1);
|
||
const limit = 10;
|
||
|
||
const fetchPets = async () => {
|
||
try {
|
||
setIsLoading(true);
|
||
const res = await api.get('/admin/pets', { params: { page, limit, search } });
|
||
if (res.data?.data) {
|
||
setPets(res.data.data);
|
||
setTotalPages(res.data.meta?.lastPage || 1);
|
||
}
|
||
} catch (err) {
|
||
console.error(err);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
const timer = setTimeout(() => {
|
||
fetchPets();
|
||
}, 500);
|
||
return () => clearTimeout(timer);
|
||
}, [search, page]);
|
||
|
||
const handleDelete = async (id: string) => {
|
||
if (!window.confirm('آیا از حذف پروفایل این حیوان خانگی مطمئن هستید؟')) return;
|
||
try {
|
||
await api.delete(`/admin/pets/${id}`);
|
||
fetchPets();
|
||
} catch (error) {
|
||
console.error('Delete failed', error);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<div className="flex flex-col sm:flex-row justify-between gap-4">
|
||
<div>
|
||
<h2 className="text-2xl font-black text-gray-900 flex items-center gap-2">
|
||
<Heart className="w-6 h-6 text-purple-600 fill-purple-100" />
|
||
حیوانات خانگی کاربران (Pets)
|
||
</h2>
|
||
<p className="text-gray-500 font-medium mt-1">مشاهده و مدیریت پروفایل حیوانات خانگی ثبت شده توسط کاربران</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 rounded-2xl shadow-sm border border-gray-200 flex flex-col sm:flex-row gap-4">
|
||
<div className="relative flex-1">
|
||
<Search className="w-5 h-5 absolute right-4 top-1/2 -translate-y-1/2 text-gray-400" />
|
||
<input
|
||
type="text"
|
||
placeholder="جستجو در نام حیوان..."
|
||
value={search}
|
||
onChange={(e) => { setSearch(e.target.value); setPage(1); }}
|
||
className="w-full pl-4 pr-12 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full text-right">
|
||
<thead className="bg-gray-50 border-b border-gray-100">
|
||
<tr>
|
||
<th className="py-4 px-6 text-sm font-bold text-gray-500">تصویر</th>
|
||
<th className="py-4 px-6 text-sm font-bold text-gray-500">نام و نژاد</th>
|
||
<th className="py-4 px-6 text-sm font-bold text-gray-500">صاحب حیوان</th>
|
||
<th className="py-4 px-6 text-sm font-bold text-gray-500">مشخصات فیزیکی</th>
|
||
<th className="py-4 px-6 text-sm font-bold text-gray-500 w-24">عملیات</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-gray-100">
|
||
{isLoading ? (
|
||
<tr><td colSpan={5} className="py-12 text-center"><Spinner size="lg" className="mx-auto text-purple-600" /></td></tr>
|
||
) : pets.length === 0 ? (
|
||
<tr><td colSpan={5} className="py-12 text-center text-gray-500 font-medium">هیچ حیوانی یافت نشد</td></tr>
|
||
) : (
|
||
pets.map((pet) => (
|
||
<tr key={pet.id} className="hover:bg-gray-50/50 transition-colors">
|
||
<td className="py-3 px-6">
|
||
{pet.imageUrl ? (
|
||
<img src={pet.imageUrl.startsWith('http') ? pet.imageUrl : `${BASE_DOMAIN}${pet.imageUrl}`} alt={pet.name} className="w-12 h-12 rounded-full object-cover border-2 border-purple-100" />
|
||
) : (
|
||
<div className="w-12 h-12 rounded-full bg-purple-50 flex items-center justify-center text-purple-300 border-2 border-purple-100">
|
||
<ImageIcon className="w-5 h-5" />
|
||
</div>
|
||
)}
|
||
</td>
|
||
<td className="py-4 px-6">
|
||
<div className="font-bold text-gray-900">{pet.name} <span className="text-xs px-2 py-0.5 bg-gray-100 text-gray-600 rounded-lg">{pet.type}</span></div>
|
||
<div className="text-gray-500 text-sm">{pet.breed}</div>
|
||
</td>
|
||
<td className="py-4 px-6">
|
||
<div className="font-bold text-gray-700">{pet.user?.firstName} {pet.user?.lastName}</div>
|
||
<div className="text-gray-500 text-xs font-mono">{pet.user?.mobile || pet.user?.email}</div>
|
||
</td>
|
||
<td className="py-4 px-6 text-gray-600 text-sm">
|
||
{pet.age} ساله، {pet.weight} کیلوگرم<br/>
|
||
<span className="text-xs text-gray-400">تحرک: {pet.activityLevel}</span>
|
||
</td>
|
||
<td className="py-4 px-6">
|
||
<div className="flex items-center gap-2">
|
||
<button onClick={() => handleDelete(pet.id)} className="p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"><Trash2 className="w-4 h-4" /></button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
))
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{!isLoading && totalPages > 1 && (
|
||
<div className="p-4 border-t border-gray-100 flex justify-center bg-gray-50/50">
|
||
<Pagination currentPage={page} totalPages={totalPages} onPageChange={setPage} />
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|