Files
Rasadyar_Inspection_System/src/Pages/VeterinaryTransfer.tsx
2026-02-08 09:20:18 +03:30

870 lines
42 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 React, { useState } from "react";
import { motion, AnimatePresence, Variants } from "framer-motion";
import Typography from "../components/Typography/Typography";
import { Grid } from "../components/Grid/Grid";
import Textfield from "../components/Textfeild/Textfeild";
import Button from "../components/Button/Button";
import { useToast } from "../hooks/useToast";
import { useApiMutation } from "../utils/useApiRequest";
import {
DocumentTextIcon,
IdentificationIcon,
ArrowPathIcon,
} from "@heroicons/react/24/outline";
interface TransferGood {
goodCode: number;
goodAmount: number;
goodUnit: number;
goodUnitStr: string;
goodStr: string;
}
interface VeterinaryTransferItem {
trIDCode: string;
trTypeCode: number;
trTypeCodeStr: string;
trStatus: number;
trStatusStr: string;
sourcePartIDCode: string;
sourceCertID: string;
desPartIDCode: string;
desCertID: string;
issueDate: string;
issueDateStr: string;
resideDate: string;
resideDateStr: string;
listofGoods: TransferGood[];
message: string | null;
errorCode: number;
}
interface VeterinaryTransferResponse {
status: boolean;
statusCode: number;
data: VeterinaryTransferItem[];
apiLogId: string;
}
interface UnitProperty {
partIDCode: string | null;
unitPostalCode: string | null;
postalAddress: string | null;
detailAddress: string | null;
unitName: string | null;
unitGroupStr: string | null;
unitTypeStr: string | null;
licenseStatusStr: string | null;
licenseID: string | null;
ownerFullName: string | null;
ownerCompany: string | null;
lesseeFullName: string | null;
activityTypeName: string | null;
}
interface InquiryFarmData {
listUnitProperty?: UnitProperty[];
errorCode?: number;
message?: string;
}
interface InquiryFarmResponse {
status: boolean;
statusCode: number;
data: InquiryFarmData;
apiLogId: string;
}
const VeterinaryTransfer: React.FC = () => {
const [trIDCode, setTrIDCode] = useState("");
const [transferData, setTransferData] = useState<VeterinaryTransferItem[]>(
[],
);
const [farmInfoByPartId, setFarmInfoByPartId] = useState<
Record<string, InquiryFarmResponse | null>
>({});
const [farmLoadingByPartId, setFarmLoadingByPartId] = useState<
Record<string, boolean>
>({});
const showToast = useToast();
const transferMutation = useApiMutation<VeterinaryTransferResponse>({
api: "veterinary-transfer",
method: "get",
});
const inquiryFarmMutation = useApiMutation<InquiryFarmResponse>({
api: "inquiry-farm",
method: "get",
disableBackdrop: true,
});
const handleSearch = async () => {
const trimmed = trIDCode.trim();
if (!trimmed) {
showToast("لطفا کد رهگیری گواهی بهداشت حمل را وارد کنید", "error");
return;
}
try {
const result = await transferMutation.mutateAsync({ trIDCode: trimmed });
if (result?.status && Array.isArray(result?.data) && result.data.length) {
setTransferData(result.data);
} else {
setTransferData([]);
showToast("نتیجه‌ای یافت نشد", "info");
}
} catch (error: any) {
console.error("Veterinary transfer error:", error);
showToast("خطا در دریافت اطلاعات گواهی بهداشتی حمل", "error");
setTransferData([]);
}
};
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
handleSearch();
}
};
const fetchFarmInquiry = async (partIdCode: string) => {
const code = String(partIdCode || "").trim();
if (!code) return;
setFarmLoadingByPartId((prev) => ({ ...prev, [code]: true }));
try {
const res = await inquiryFarmMutation.mutateAsync({ PartIdCode: code });
setFarmInfoByPartId((prev) => ({ ...prev, [code]: res }));
if (!res?.status) {
showToast("خطا در استعلام واحد کشاورزی", "error");
}
} catch (error: any) {
console.error("Inquiry farm error:", error);
showToast("خطا در استعلام واحد کشاورزی", "error");
setFarmInfoByPartId((prev) => ({ ...prev, [code]: null }));
} finally {
setFarmLoadingByPartId((prev) => ({ ...prev, [code]: false }));
}
};
const getFirstUnitProperty = (res?: InquiryFarmResponse | null) => {
const list = res?.data?.listUnitProperty;
if (!Array.isArray(list) || list.length === 0) return null;
return list[0];
};
const cardVariants: Variants = {
hidden: { opacity: 0, y: 20, scale: 0.95 },
visible: (i: number) => ({
opacity: 1,
y: 0,
scale: 1,
transition: {
delay: i * 0.1,
duration: 0.3,
ease: "easeOut",
},
}),
exit: {
opacity: 0,
scale: 0.95,
transition: { duration: 0.2 },
},
};
return (
<Grid container column className="gap-4 p-4 max-w-6xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: 0.1 }}
className="w-full"
>
<Grid
container
column
className="gap-4 p-6 bg-white dark:bg-dark-800 rounded-xl shadow-lg border border-gray-200 dark:border-dark-600"
>
<Typography
variant="h5"
className="text-gray-900 dark:text-gray-100 font-bold mb-2"
>
استعلام گواهی بهداشتی حمل
</Typography>
<Grid container className="gap-4 items-end">
<Grid container className="flex-1" column>
<Textfield
placeholder="کد رهگیری گواهی بهداشت حمل"
value={trIDCode}
onChange={(e) => setTrIDCode(e.target.value)}
onKeyPress={handleKeyPress}
fullWidth
/>
</Grid>
<Button
onClick={handleSearch}
disabled={transferMutation.isPending}
className="px-6 py-2.5"
>
{transferMutation.isPending ? "در حال جستجو..." : "جستجو"}
</Button>
</Grid>
</Grid>
</motion.div>
<AnimatePresence mode="popLayout">
{transferData.length > 0 && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="w-full"
>
<Typography
variant="body1"
className="mb-4 text-gray-700 dark:text-gray-300 font-semibold"
>
نتایج جستجو ({transferData.length})
</Typography>
<Grid container column className="gap-6">
{transferData.map((item, index) => {
const sourceCode = String(item.sourcePartIDCode || "").trim();
const destCode = String(item.desPartIDCode || "").trim();
const sourceInquiry = farmInfoByPartId[sourceCode];
const destInquiry = farmInfoByPartId[destCode];
const sourceUnit = getFirstUnitProperty(sourceInquiry);
const destUnit = getFirstUnitProperty(destInquiry);
return (
<motion.div
key={`${item.trIDCode}-${index}`}
custom={index}
variants={cardVariants}
initial="hidden"
animate="visible"
exit="exit"
className="w-full"
>
<div className="bg-white dark:bg-dark-800 rounded-xl shadow-lg border border-gray-200 dark:border-dark-600 p-6 hover:shadow-xl transition-shadow">
<Grid container column className="gap-4">
<div className="flex items-start justify-between pb-4 border-b border-gray-200 dark:border-dark-600">
<div className="flex items-center gap-3">
<div className="p-3 bg-primary-100 dark:bg-primary-900/30 rounded-lg">
<DocumentTextIcon className="w-6 h-6 text-primary-600 dark:text-primary-400" />
</div>
<div>
<Typography
variant="h6"
className="text-gray-900 dark:text-gray-100 font-bold"
>
ردیف {index + 1}
</Typography>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
کد رهگیری گواهی بهداشتی حمل
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium break-all"
>
{item.trIDCode}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نوع گواهی بهداشتی حمل
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.trTypeCodeStr}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
وضعیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.trStatusStr}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شماره مجوز مبداء
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.sourceCertID || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شماره مجوز مقصد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.desCertID || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
تاریخ صدور گواهی بهداشتی حمل
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.issueDateStr}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
تاریخ تغییر وضعیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{item.resideDateStr}
</Typography>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شناسه یکتای واحدهای کشاورزی مبداء
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium break-all"
>
{item.sourcePartIDCode}
</Typography>
</div>
<Button
onClick={() => fetchFarmInquiry(sourceCode)}
disabled={!!farmLoadingByPartId[sourceCode]}
className="px-4 py-2"
>
<span className="flex items-center gap-2">
<ArrowPathIcon className="w-5 h-5" />
{farmLoadingByPartId[sourceCode]
? "..."
: "استعلام"}
</span>
</Button>
</div>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شناسه یکتای واحدهای کشاورزی مقصد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium break-all"
>
{item.desPartIDCode}
</Typography>
</div>
<Button
onClick={() => fetchFarmInquiry(destCode)}
disabled={!!farmLoadingByPartId[destCode]}
className="px-4 py-2"
>
<span className="flex items-center gap-2">
<ArrowPathIcon className="w-5 h-5" />
{farmLoadingByPartId[destCode]
? "..."
: "استعلام"}
</span>
</Button>
</div>
</div>
</div>
{item.listofGoods && item.listofGoods.length > 0 && (
<div className="pt-2">
<div className="flex items-center gap-2 mb-3">
<DocumentTextIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300 font-semibold"
>
لیست کالا
</Typography>
</div>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100 dark:bg-dark-700">
<th className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-center text-sm font-semibold text-gray-700 dark:text-gray-300">
ردیف
</th>
<th className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-center text-sm font-semibold text-gray-700 dark:text-gray-300">
نام کالا
</th>
<th className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-center text-sm font-semibold text-gray-700 dark:text-gray-300">
مقدار
</th>
<th className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-center text-sm font-semibold text-gray-700 dark:text-gray-300">
واحد
</th>
</tr>
</thead>
<tbody>
{item.listofGoods.map((g, idx) => (
<tr
key={idx}
className="hover:bg-gray-50 dark:hover:bg-dark-700"
>
<td className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-sm text-gray-900 dark:text-gray-100 text-center">
{idx + 1}
</td>
<td className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-sm text-gray-900 dark:text-gray-100 text-center">
{g.goodStr}
</td>
<td className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-sm text-gray-900 dark:text-gray-100 text-center">
{Number(g.goodAmount).toLocaleString()}
</td>
<td className="border border-gray-300 dark:border-dark-600 px-4 py-2 text-sm text-gray-900 dark:text-gray-100 text-center">
{g.goodUnitStr}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{(sourceUnit || destUnit) && (
<div className="pt-2">
{sourceUnit && (
<div className="mb-6">
<Typography
variant="body1"
className="text-gray-700 dark:text-gray-300 font-semibold mb-3"
>
شناسه یکتای واحد کشاورزی (مبداء)
</Typography>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
کد پستی
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.unitPostalCode || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
جزئیات آدرس
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.detailAddress ||
sourceUnit.postalAddress ||
"-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.unitName || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
طبقه بندی نوع واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.unitGroupStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نوع واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.unitTypeStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
وضعیت پروانه بهره برداری/مجوز فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.licenseStatusStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شماره پروانه بهره برداری/مجوز فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.licenseID || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام مالک/بهره بردار
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.ownerFullName || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام شرکت مالک/بهره بردار
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.ownerCompany || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام مستاجر
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.lesseeFullName?.trim() || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
عنوان فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{sourceUnit.activityTypeName || "-"}
</Typography>
</div>
</div>
</div>
)}
{destUnit && (
<div>
<Typography
variant="body1"
className="text-gray-700 dark:text-gray-300 font-semibold mb-3"
>
شناسه یکتای واحد کشاورزی (مقصد)
</Typography>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
کد پستی
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.unitPostalCode || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
جزئیات آدرس
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.detailAddress ||
destUnit.postalAddress ||
"-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.unitName || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
طبقه بندی نوع واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.unitGroupStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نوع واحد
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.unitTypeStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
وضعیت پروانه بهره برداری/مجوز فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.licenseStatusStr || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
شماره پروانه بهره برداری/مجوز فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.licenseID || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام مالک/بهره بردار
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.ownerFullName || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام شرکت مالک/بهره بردار
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.ownerCompany || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
نام مستاجر
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.lesseeFullName?.trim() || "-"}
</Typography>
</div>
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<Typography
variant="body2"
className="text-gray-500 dark:text-gray-400 text-xs"
>
عنوان فعالیت
</Typography>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium"
>
{destUnit.activityTypeName || "-"}
</Typography>
</div>
</div>
</div>
)}
</div>
)}
{item.message && (
<div className="p-3 bg-gray-50 dark:bg-dark-700 rounded-lg">
<div className="flex items-center gap-2">
<IdentificationIcon className="w-5 h-5 text-gray-600 dark:text-gray-400" />
<Typography
variant="body2"
className="text-gray-700 dark:text-gray-300 font-semibold"
>
پیام
</Typography>
</div>
<Typography
variant="body1"
className="text-gray-900 dark:text-gray-100 font-medium mt-2"
>
{item.message}
</Typography>
</div>
)}
</Grid>
</div>
</motion.div>
);
})}
</Grid>
</motion.div>
)}
</AnimatePresence>
</Grid>
);
};
export default VeterinaryTransfer;