Files
gastro-admin/src/features/plans/ui/PalanTable.tsx
Samandar Turgunboyev c9cf9bb725 vercel deploy
2025-12-23 10:01:09 +05:00

230 lines
6.9 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.

"use client";
import { plans_api } from "@/features/plans/lib/api";
import type { Product } from "@/features/plans/lib/data";
import { API_URLS } from "@/shared/config/api/URLs";
import formatPrice from "@/shared/lib/formatPrice";
import { Button } from "@/shared/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/ui/select";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/shared/ui/table";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import clsx from "clsx";
import { Edit, Eye, Loader2, Trash } from "lucide-react";
import type { Dispatch, SetStateAction } from "react";
import { toast } from "sonner";
interface Props {
products: Product[] | [];
isLoading: boolean;
isFetching: boolean;
isError: boolean;
setEditingProduct: Dispatch<SetStateAction<Product | null>>;
setDetailOpen: Dispatch<SetStateAction<boolean>>;
setDialogOpen: Dispatch<SetStateAction<boolean>>;
handleDelete: (product: Product) => void;
}
const ProductTable = ({
products,
isLoading,
isFetching,
isError,
setEditingProduct,
setDetailOpen,
setDialogOpen,
handleDelete,
}: Props) => {
const { data } = useQuery({
queryKey: ["categories"],
queryFn: () => {
return plans_api.categories({ page: 1, page_size: 1000 });
},
select(data) {
return data.data.results;
},
});
const { data: unityData } = useQuery({
queryKey: ["unity"],
queryFn: () => {
return plans_api.unity({ page: 1, page_size: 1000 });
},
select(data) {
return data.data.results;
},
});
const queryClient = useQueryClient();
const { mutate: updated } = useMutation({
mutationFn: ({ body, id }: { id: string; body: FormData }) =>
plans_api.update({ body, id }),
onSuccess() {
toast.success("Mahsulot statusi o'zgardi", {
richColors: true,
position: "top-center",
});
queryClient.refetchQueries({ queryKey: ["product_list"] });
setDialogOpen(false);
},
onError: (err: AxiosError) => {
toast.error((err.response?.data as string) || "Xatolik yuz berdi", {
richColors: true,
position: "top-center",
});
},
});
const handleStatusChange = async (
product: string,
status: "true" | "false",
) => {
const formData = new FormData();
formData.append("is_active", status);
updated({ body: formData, id: product });
};
if (isLoading || isFetching) {
return (
<div className="flex h-full items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
);
}
if (isError) {
return (
<div className="flex h-full items-center justify-center text-red-600">
Maʼlumotlarni yuklashda xatolik yuz berdi
</div>
);
}
return (
<div className="overflow-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Rasmi</TableHead>
<TableHead>Nomi (UZ)</TableHead>
<TableHead>Nomi (RU)</TableHead>
<TableHead>Tavsif (UZ)</TableHead>
<TableHead>Tavsif (RU)</TableHead>
<TableHead>Kategoriya</TableHead>
<TableHead>Birligi</TableHead>
<TableHead>Brendi</TableHead>
<TableHead>Narx</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Harakatlar</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.map((product, index) => {
const cat = data?.find((c) => c.id === product.category);
const unity = unityData?.find((u) => u.id === product.unity);
return (
<TableRow key={product.id}>
<TableCell>{index + 1}</TableCell>
<TableCell>
<img
src={API_URLS.BASE_URL + product.image}
alt={product.name_uz}
className="w-16 h-16 object-cover rounded"
/>
</TableCell>
<TableCell>{product.name_uz}</TableCell>
<TableCell>{product.name_ru}</TableCell>
<TableCell>{product.description_uz.slice(0, 15)}...</TableCell>
<TableCell>{product.description_ru.slice(0, 15)}...</TableCell>
<TableCell>{cat?.name_uz}</TableCell>
<TableCell>{unity?.name_uz}</TableCell>
<TableCell>{product.brand}</TableCell>
<TableCell>{formatPrice(product.price, true)}</TableCell>
<TableCell
className={clsx(
product.is_active ? "text-green-600" : "text-red-600",
)}
>
<Select
value={product.is_active ? "true" : "false"}
onValueChange={(value) =>
handleStatusChange(product.id, value as "true" | "false")
}
>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Holati" />
</SelectTrigger>
<SelectContent>
<SelectItem value="true">Faol</SelectItem>
<SelectItem value="false">Nofaol</SelectItem>
</SelectContent>
</Select>
</TableCell>
<TableCell className="space-x-2 text-right">
<Button
size="sm"
variant="outline"
onClick={() => {
setEditingProduct(product);
setDetailOpen(true);
}}
>
<Eye className="h-4 w-4" />
</Button>
<Button
size="sm"
className="bg-blue-500 text-white hover:bg-blue-600"
onClick={() => {
setEditingProduct(product);
setDialogOpen(true);
}}
>
<Edit className="h-4 w-4" />
</Button>
<Button
size="sm"
variant="destructive"
onClick={() => handleDelete(product)}
>
<Trash className="h-4 w-4" />
</Button>
</TableCell>
</TableRow>
);
})}
{products.length === 0 && (
<TableRow>
<TableCell colSpan={8} className="text-center text-gray-500">
Mahsulotlar topilmadi
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
);
};
export default ProductTable;