Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8d2da4f28 | |||
| bb1d5b3315 | |||
| d8d415a8f5 | |||
| f58c8e6c58 | |||
| 3a17fcb448 | |||
| 6e219aca1a | |||
| 9b74be078f | |||
| 5fd55c4b10 | |||
| 6b5276ce36 | |||
| e465843eb9 | |||
| e5402f9037 | |||
| e342a7cdd5 | |||
| f5de2f68b5 | |||
| 44ea5974eb | |||
| cb87251d62 | |||
| a1b430ad8e | |||
| 7b88a664b0 | |||
| 2a6d978dba | |||
| b9f9e6cd06 | |||
| de31fa9e6d | |||
| a705d0360b | |||
| f1ba276c6c | |||
| 983a072487 | |||
| 0c951f7b4c | |||
| 4a719c9d1c | |||
| bb1b22152a | |||
| de16f203d4 | |||
| e0633245cd | |||
| 576fc434dc | |||
| cfc4b8cc53 | |||
| 3550e1fec7 | |||
| b573a16e89 |
@@ -1,4 +1,4 @@
|
|||||||
FROM node:18-alpine
|
FROM registry.hamdocker.ir/seniorkian/node:18-alpine
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
38
src/Pages/TagDistribution.tsx
Normal file
38
src/Pages/TagDistribution.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Grid } from "../components/Grid/Grid";
|
||||||
|
import Tabs from "../components/Tab/Tab";
|
||||||
|
import TagActiveDistributions from "../partials/tagging/TagActiveDistributions";
|
||||||
|
import TagCanceledDistributions from "../partials/tagging/TagCanceledDistributions";
|
||||||
|
|
||||||
|
export default function TagDistribtution() {
|
||||||
|
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||||
|
const handleTabChange = (index: number) => {
|
||||||
|
setSelectedTab(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabItems = [
|
||||||
|
{
|
||||||
|
label: "توزیع فعال",
|
||||||
|
page: "tag_distribution",
|
||||||
|
access: "Tag-Distribution-Actives",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "توزیع های لغو شده",
|
||||||
|
page: "tag_distribution",
|
||||||
|
access: "Tag-Distribution-Cancels",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container column className="justify-center mt-2">
|
||||||
|
<Tabs tabs={tabItems} onChange={handleTabChange} size="medium" />
|
||||||
|
<Grid container column className="mt-2">
|
||||||
|
{selectedTab === 0 ? (
|
||||||
|
<TagActiveDistributions />
|
||||||
|
) : (
|
||||||
|
<TagCanceledDistributions />
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
150
src/Pages/TagDistributionDetails.tsx
Normal file
150
src/Pages/TagDistributionDetails.tsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { useParams } from "@tanstack/react-router";
|
||||||
|
import { Bars3Icon, CubeIcon, SparklesIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { useApiRequest } from "../utils/useApiRequest";
|
||||||
|
import { formatJustDate, formatJustTime } from "../utils/formatTime";
|
||||||
|
import ShowMoreInfo from "../components/ShowMoreInfo/ShowMoreInfo";
|
||||||
|
import { Grid } from "../components/Grid/Grid";
|
||||||
|
import Typography from "../components/Typography/Typography";
|
||||||
|
import Table from "../components/Table/Table";
|
||||||
|
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TagDistribtutionDetails() {
|
||||||
|
const { id } = useParams({ strict: false });
|
||||||
|
|
||||||
|
const { data } = useApiRequest({
|
||||||
|
api: `/tag/web/api/v1/tag_distribution_batch/${id}/`,
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagBatchInnerDashboard", id],
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dist = data?.distributions;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container column className="gap-4 mt-2">
|
||||||
|
<Grid isDashboard>
|
||||||
|
<Table
|
||||||
|
isDashboard
|
||||||
|
title="مشخصات توزیع پلاک"
|
||||||
|
noSearch
|
||||||
|
noPagination
|
||||||
|
columns={[
|
||||||
|
"شناسه توزیع",
|
||||||
|
"تاریخ ثبت",
|
||||||
|
"توزیع کننده",
|
||||||
|
"دریافت کننده",
|
||||||
|
"تعداد کل پلاک",
|
||||||
|
"پلاک های توزیع شده",
|
||||||
|
"پلاک های باقیمانده",
|
||||||
|
"نوع توزیع",
|
||||||
|
"جزئیات توزیع",
|
||||||
|
]}
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
data?.dist_batch_identity ?? "-",
|
||||||
|
`${formatJustDate(data?.create_date) ?? "-"} (${
|
||||||
|
formatJustDate(data?.create_date)
|
||||||
|
? (formatJustTime(data?.create_date) ?? "-")
|
||||||
|
: "-"
|
||||||
|
})`,
|
||||||
|
data?.assigner_org?.name ?? "-",
|
||||||
|
data?.assigned_org?.name ?? "-",
|
||||||
|
data?.total_tag_count?.toLocaleString() ?? "-",
|
||||||
|
data?.total_distributed_tag_count?.toLocaleString() ?? "-",
|
||||||
|
data?.remaining_tag_count?.toLocaleString() ?? "-",
|
||||||
|
data?.distribution_type === "batch"
|
||||||
|
? "توزیع گروهی"
|
||||||
|
: "توزیع تصادفی",
|
||||||
|
<ShowMoreInfo key={data?.id} title="جزئیات توزیع">
|
||||||
|
<Grid container column className="gap-4 w-full">
|
||||||
|
{dist?.map((opt: any, index: number) => (
|
||||||
|
<Grid
|
||||||
|
key={index}
|
||||||
|
container
|
||||||
|
column
|
||||||
|
className="gap-3 w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4"
|
||||||
|
>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<SparklesIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
گونه:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{speciesMap[opt?.species_code] ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{data?.distribution_type === "batch" &&
|
||||||
|
opt?.serial_from != null && (
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<Bars3Icon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
بازه سریال:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-600 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
از {opt?.serial_from ?? "-"} تا{" "}
|
||||||
|
{opt?.serial_to ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
تعداد پلاک:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.total_tag_count?.toLocaleString() ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
پلاک های توزیع شده:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.distributed_number?.toLocaleString() ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
پلاک های باقیمانده:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.remaining_number?.toLocaleString() ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</ShowMoreInfo>,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -10,24 +10,38 @@ import { SubmitNewTags } from "../partials/tagging/SubmitNewTags";
|
|||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { TAGS } from "../routes/paths";
|
import { TAGS } from "../routes/paths";
|
||||||
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons";
|
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons";
|
||||||
|
import { TableButton } from "../components/TableButton/TableButton";
|
||||||
|
import AutoComplete from "../components/AutoComplete/AutoComplete";
|
||||||
|
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
|
||||||
export default function Tagging() {
|
export default function Tagging() {
|
||||||
const { openModal } = useModalStore();
|
const { openModal } = useModalStore();
|
||||||
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
||||||
const [tagsTableData, setTagsTableData] = useState([]);
|
const [tagsTableData, setTagsTableData] = useState<any[]>([]);
|
||||||
|
const [selectedSpecie, setSelectedSpecie] = useState<
|
||||||
|
(string | number)[] | any
|
||||||
|
>([]);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { data: tagsData, refetch } = useApiRequest({
|
const { data: tagsData, refetch } = useApiRequest({
|
||||||
api: "/tag/web/api/v1/tag_batch/",
|
api: `/tag/web/api/v1/tag_batch/?species_code=${
|
||||||
|
selectedSpecie.length ? selectedSpecie[0] : ""
|
||||||
|
}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
queryKey: ["tagsList", tableInfo],
|
queryKey: ["tagsList", tableInfo, selectedSpecie],
|
||||||
params: {
|
params: { ...tableInfo },
|
||||||
...tableInfo,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: tagDashboardData, refetch: updateDashboard } = useApiRequest({
|
const { data: tagDashboardData, refetch: updateDashboard } = useApiRequest({
|
||||||
api: "/tag/web/api/v1/tag/tag_dashboard/",
|
api: "/tag/web/api/v1/tag_batch/main_dashboard/",
|
||||||
method: "get",
|
method: "get",
|
||||||
queryKey: ["tagDashboard"],
|
queryKey: ["tagDashboard"],
|
||||||
});
|
});
|
||||||
@@ -58,6 +72,8 @@ export default function Tagging() {
|
|||||||
: "نامشخص",
|
: "نامشخص",
|
||||||
item?.serial_from || "-",
|
item?.serial_from || "-",
|
||||||
item?.serial_to || "-",
|
item?.serial_to || "-",
|
||||||
|
item?.total_distributed_tags || 0,
|
||||||
|
item?.total_remaining_tags || 0,
|
||||||
<Popover key={item.id}>
|
<Popover key={item.id}>
|
||||||
<Tooltip title="مشاهده پلاک ها" position="right">
|
<Tooltip title="مشاهده پلاک ها" position="right">
|
||||||
<Button
|
<Button
|
||||||
@@ -77,6 +93,21 @@ export default function Tagging() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip title="ویرایش" position="right">
|
||||||
|
<Button
|
||||||
|
variant="edit"
|
||||||
|
page="livestock_farmers"
|
||||||
|
access="Edit-Rancher"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "ایجاد پلاک جدید",
|
||||||
|
content: (
|
||||||
|
<SubmitNewTags getData={handleUpdate} item={item} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<DeleteButtonForPopOver
|
<DeleteButtonForPopOver
|
||||||
page="tagging"
|
page="tagging"
|
||||||
access="Delete-Tag"
|
access="Delete-Tag"
|
||||||
@@ -90,10 +121,26 @@ export default function Tagging() {
|
|||||||
} else {
|
} else {
|
||||||
setTagsTableData([]);
|
setTagsTableData([]);
|
||||||
}
|
}
|
||||||
}, [tagsData]);
|
}, [tagsData, tableInfo.page, tableInfo.page_size, refetch]);
|
||||||
|
|
||||||
|
const { data: speciesData } = useApiRequest({
|
||||||
|
api: "/livestock/web/api/v1/livestock_species",
|
||||||
|
method: "get",
|
||||||
|
params: { page: 1, pageSize: 1000 },
|
||||||
|
queryKey: ["species"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const speciesOptions = () => {
|
||||||
|
return speciesData?.results?.map((opt: any) => {
|
||||||
|
return {
|
||||||
|
key: opt?.value,
|
||||||
|
value: opt?.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container column className="gap-4 mt-2">
|
<Grid container column className="gap-4 mt-2 rtl">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
@@ -118,30 +165,112 @@ export default function Tagging() {
|
|||||||
noPagination
|
noPagination
|
||||||
noSearch
|
noSearch
|
||||||
columns={[
|
columns={[
|
||||||
"تعداد کل",
|
"تعداد گروه پلاک",
|
||||||
"تعداد پلاک های آزاد",
|
"پلاکهای تولیدشده",
|
||||||
"تعداد پلاک شده",
|
"گروه پلاک های دارای توزیع",
|
||||||
"گاو",
|
"پلاک توزیع شده",
|
||||||
"گاومیش",
|
"پلاک باقیمانده",
|
||||||
"شتر",
|
"جزئیات گونه ها",
|
||||||
"گوسفند",
|
|
||||||
"بز",
|
|
||||||
]}
|
]}
|
||||||
rows={[
|
rows={[
|
||||||
[
|
[
|
||||||
tagDashboardData?.count?.toLocaleString() || 0,
|
tagDashboardData?.batch_count?.toLocaleString() || 0,
|
||||||
tagDashboardData?.free_count?.toLocaleString() || 0,
|
tagDashboardData?.tag_count_created_by_batch?.toLocaleString() ||
|
||||||
tagDashboardData?.assign_count?.toLocaleString() || 0,
|
0,
|
||||||
tagDashboardData?.cow_count?.toLocaleString() || 0,
|
tagDashboardData?.has_distributed_batches_number?.toLocaleString() ||
|
||||||
tagDashboardData?.buffalo_count?.toLocaleString() || 0,
|
0,
|
||||||
tagDashboardData?.camel_count?.toLocaleString() || 0,
|
tagDashboardData?.total_distributed_tags?.toLocaleString() || 0,
|
||||||
tagDashboardData?.sheep_count?.toLocaleString() || 0,
|
tagDashboardData?.total_remaining_tags?.toLocaleString() || 0,
|
||||||
tagDashboardData?.goat_count?.toLocaleString() || 0,
|
<TableButton
|
||||||
|
key="species-stats"
|
||||||
|
size="small"
|
||||||
|
onClick={() =>
|
||||||
|
openModal({
|
||||||
|
title: "آمار گونهای",
|
||||||
|
isFullSize: true,
|
||||||
|
content: (
|
||||||
|
<BatchBySpeciesModal
|
||||||
|
batchData={
|
||||||
|
tagDashboardData?.batch_data_by_species || []
|
||||||
|
}
|
||||||
|
onRowAction={(row) => {
|
||||||
|
openModal({
|
||||||
|
title: `جزئیات ${
|
||||||
|
speciesMap[row?.species_code] ?? "-"
|
||||||
|
}`,
|
||||||
|
content: (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
تعداد بچ
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.batch_count?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک تولید شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.tag_count_created_by_batch?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک توزیع شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_distributed_tags?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک باقیمانده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_remaining_tags?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
بچهای توزیعشده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.has_distributed_batches_number?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
مشاهده
|
||||||
|
</TableButton>,
|
||||||
],
|
],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid container className="items-center gap-2">
|
||||||
|
<Grid>
|
||||||
|
{speciesOptions() && (
|
||||||
|
<AutoComplete
|
||||||
|
data={speciesOptions()}
|
||||||
|
selectedKeys={selectedSpecie}
|
||||||
|
onChange={setSelectedSpecie}
|
||||||
|
title="گونه"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
<Table
|
<Table
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onChange={setTableInfo}
|
onChange={setTableInfo}
|
||||||
@@ -151,9 +280,11 @@ export default function Tagging() {
|
|||||||
columns={[
|
columns={[
|
||||||
"ردیف",
|
"ردیف",
|
||||||
"سازمان ثبت کننده",
|
"سازمان ثبت کننده",
|
||||||
"کد گونه",
|
"گونه",
|
||||||
"از بازه سریال",
|
"از بازه سریال",
|
||||||
"تا بازه سریال",
|
"تا بازه سریال",
|
||||||
|
"پلاک های توزیع شده",
|
||||||
|
"پلاک های باقیمانده",
|
||||||
"عملیات",
|
"عملیات",
|
||||||
]}
|
]}
|
||||||
rows={tagsTableData}
|
rows={tagsTableData}
|
||||||
@@ -161,3 +292,78 @@ export default function Tagging() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BatchBySpeciesModal({
|
||||||
|
batchData = [],
|
||||||
|
}: {
|
||||||
|
batchData: Array<any>;
|
||||||
|
onRowAction?: (row: any, index: number) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{batchData?.map((row, idx) => {
|
||||||
|
const speciesName = speciesMap[row?.species_code] ?? "-";
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-sm p-4 flex flex-col"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-primary/70"></div>
|
||||||
|
<span className="text-sm font-bold text-gray-900 dark:text-gray-100">
|
||||||
|
{speciesName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
تعداد گروه پلاک
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.batch_count?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
گروه پلاک های توزیعشده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.has_distributed_batches_number?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک تولید شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.tag_count_created_by_batch?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک توزیع شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_distributed_tags?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک باقیمانده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_remaining_tags?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,23 +9,44 @@ import { Tooltip } from "../components/Tooltip/Tooltip";
|
|||||||
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons";
|
import { DeleteButtonForPopOver } from "../components/PopOverButtons/PopOverButtons";
|
||||||
import { TagDetails } from "../partials/tagging/TagDetails";
|
import { TagDetails } from "../partials/tagging/TagDetails";
|
||||||
import { useParams } from "@tanstack/react-router";
|
import { useParams } from "@tanstack/react-router";
|
||||||
|
import { TableButton } from "../components/TableButton/TableButton";
|
||||||
|
import AutoComplete from "../components/AutoComplete/AutoComplete";
|
||||||
|
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusOptions = [
|
||||||
|
{ key: "F", value: "آزاد" },
|
||||||
|
{ key: "A", value: "پلاک شده" },
|
||||||
|
{ key: "R", value: "رزرو" },
|
||||||
|
];
|
||||||
|
|
||||||
export default function Tags() {
|
export default function Tags() {
|
||||||
const { openModal } = useModalStore();
|
const { openModal } = useModalStore();
|
||||||
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
||||||
const [tagsTableData, setTagsTableData] = useState([]);
|
const [tagsTableData, setTagsTableData] = useState([]);
|
||||||
const { id, from, to } = useParams({ strict: false });
|
const { id, from, to } = useParams({ strict: false });
|
||||||
|
const [selectedStatus, setSelectedStatus] = useState<(string | number)[]>([]);
|
||||||
|
|
||||||
const { data: tagsData, refetch } = useApiRequest({
|
const { data: tagsData, refetch } = useApiRequest({
|
||||||
api: `/tag/web/api/v1/tag/${id ? id + "/tags_by_batch" : ""}`,
|
api: `/tag/web/api/v1/tag/${id ? id + "/tags_by_batch" : ""}`,
|
||||||
method: "get",
|
method: "get",
|
||||||
queryKey: ["tagsList", tableInfo],
|
queryKey: ["tagsList", tableInfo, selectedStatus],
|
||||||
params: {
|
params: {
|
||||||
...tableInfo,
|
...tableInfo,
|
||||||
|
status: selectedStatus.length ? selectedStatus[0] : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: tagDashboardData } = useApiRequest({
|
const { data: tagDashboardData } = useApiRequest({
|
||||||
api: "/tag/web/api/v1/tag/tag_dashboard/",
|
api: id
|
||||||
|
? `/tag/web/api/v1/tag_batch/${id}/inner_dashboard`
|
||||||
|
: "/tag/web/api/v1/tag/tag_dashboard/",
|
||||||
method: "get",
|
method: "get",
|
||||||
queryKey: ["tagDashboard"],
|
queryKey: ["tagDashboard"],
|
||||||
});
|
});
|
||||||
@@ -89,13 +110,24 @@ export default function Tags() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container column className="gap-4 mt-2">
|
<Grid container column className="gap-4 mt-2">
|
||||||
|
{tagDashboardData && (
|
||||||
<Grid isDashboard>
|
<Grid isDashboard>
|
||||||
<Table
|
<Table
|
||||||
isDashboard
|
isDashboard
|
||||||
title="خلاصه اطلاعات"
|
title="خلاصه اطلاعات"
|
||||||
noPagination
|
noPagination
|
||||||
noSearch
|
noSearch
|
||||||
columns={[
|
columns={
|
||||||
|
id
|
||||||
|
? [
|
||||||
|
"تعداد پلاک",
|
||||||
|
"پلاک های توزیع شده",
|
||||||
|
"تعداد پلاک های گروه پلاک",
|
||||||
|
"تعداد پلاک های توزیع شده",
|
||||||
|
"تعداد پلاک های باقیمانده",
|
||||||
|
"آمار گونه ای",
|
||||||
|
]
|
||||||
|
: [
|
||||||
"تعداد کل",
|
"تعداد کل",
|
||||||
"تعداد پلاک های آزاد",
|
"تعداد پلاک های آزاد",
|
||||||
"تعداد پلاک شده",
|
"تعداد پلاک شده",
|
||||||
@@ -104,8 +136,99 @@ export default function Tags() {
|
|||||||
"شتر",
|
"شتر",
|
||||||
"گوسفند",
|
"گوسفند",
|
||||||
"بز",
|
"بز",
|
||||||
]}
|
]
|
||||||
rows={[
|
}
|
||||||
|
rows={
|
||||||
|
id
|
||||||
|
? [
|
||||||
|
[
|
||||||
|
tagDashboardData?.batch_count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.has_distributed_batches_number?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
tagDashboardData?.tag_count_created_by_batch?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
tagDashboardData?.total_distributed_tags?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
tagDashboardData?.total_remaining_tags?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
<TableButton
|
||||||
|
key="species-stats"
|
||||||
|
size="small"
|
||||||
|
onClick={() =>
|
||||||
|
openModal({
|
||||||
|
title: "آمار گونهای",
|
||||||
|
isFullSize: true,
|
||||||
|
content: (
|
||||||
|
<BatchBySpeciesModal
|
||||||
|
batchData={
|
||||||
|
tagDashboardData?.batch_data_by_species || []
|
||||||
|
}
|
||||||
|
onRowAction={(row) => {
|
||||||
|
openModal({
|
||||||
|
title: `جزئیات ${
|
||||||
|
speciesMap[row?.species_code] ?? "-"
|
||||||
|
}`,
|
||||||
|
content: (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
تعداد بچ
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.batch_count?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک تولید شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.tag_count_created_by_batch?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک توزیع شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_distributed_tags?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک باقیمانده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_remaining_tags?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
بچهای توزیعشده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.has_distributed_batches_number?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
مشاهده
|
||||||
|
</TableButton>,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
: [
|
||||||
[
|
[
|
||||||
tagDashboardData?.count?.toLocaleString() || 0,
|
tagDashboardData?.count?.toLocaleString() || 0,
|
||||||
tagDashboardData?.free_count?.toLocaleString() || 0,
|
tagDashboardData?.free_count?.toLocaleString() || 0,
|
||||||
@@ -116,9 +239,22 @@ export default function Tags() {
|
|||||||
tagDashboardData?.sheep_count?.toLocaleString() || 0,
|
tagDashboardData?.sheep_count?.toLocaleString() || 0,
|
||||||
tagDashboardData?.goat_count?.toLocaleString() || 0,
|
tagDashboardData?.goat_count?.toLocaleString() || 0,
|
||||||
],
|
],
|
||||||
]}
|
]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Grid container className="items-center gap-2">
|
||||||
|
<Grid>
|
||||||
|
<AutoComplete
|
||||||
|
data={statusOptions}
|
||||||
|
selectedKeys={selectedStatus}
|
||||||
|
onChange={setSelectedStatus}
|
||||||
|
title="فیلتر پلاک ها"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
@@ -140,3 +276,78 @@ export default function Tags() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BatchBySpeciesModal({
|
||||||
|
batchData = [],
|
||||||
|
}: {
|
||||||
|
batchData: Array<any>;
|
||||||
|
onRowAction?: (row: any, index: number) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{batchData?.map((row, idx) => {
|
||||||
|
const speciesName = speciesMap[row?.species_code] ?? "-";
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-sm p-4 flex flex-col"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-primary/70"></div>
|
||||||
|
<span className="text-sm font-bold text-gray-900 dark:text-gray-100">
|
||||||
|
{speciesName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
تعداد گروه پلاک
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.batch_count?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
گروه پلاک های توزیعشده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.has_distributed_batches_number?.toLocaleString?.() ??
|
||||||
|
0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک تولید شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.tag_count_created_by_batch?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک توزیع شده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_distributed_tags?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-gray-600 dark:text-gray-300">
|
||||||
|
پلاک باقیمانده
|
||||||
|
</span>
|
||||||
|
<span className="font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{row?.total_remaining_tags?.toLocaleString?.() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface AutoCompleteProps {
|
|||||||
multiselect?: boolean;
|
multiselect?: boolean;
|
||||||
inPage?: boolean;
|
inPage?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
selectedKeys: (number | string)[];
|
selectedKeys: (number | string)[] | any;
|
||||||
onChange: (keys: (number | string)[]) => void | [];
|
onChange: (keys: (number | string)[]) => void | [];
|
||||||
width?: string;
|
width?: string;
|
||||||
buttonHeight?: number | string;
|
buttonHeight?: number | string;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import React, {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {
|
import {
|
||||||
|
ArrowUpCircleIcon,
|
||||||
ChartBarIcon,
|
ChartBarIcon,
|
||||||
DocumentChartBarIcon,
|
DocumentChartBarIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
@@ -56,7 +57,8 @@ type ButtonProps = {
|
|||||||
| "view"
|
| "view"
|
||||||
| "info"
|
| "info"
|
||||||
| "chart"
|
| "chart"
|
||||||
| "set";
|
| "set"
|
||||||
|
| "share";
|
||||||
page?: string;
|
page?: string;
|
||||||
access?: string;
|
access?: string;
|
||||||
height?: string | number;
|
height?: string | number;
|
||||||
@@ -161,6 +163,10 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
return (
|
return (
|
||||||
<WrenchIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" />
|
<WrenchIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" />
|
||||||
);
|
);
|
||||||
|
case "share":
|
||||||
|
return (
|
||||||
|
<ArrowUpCircleIcon className="w-5 h-5 text-purple-400 dark:text-purple-100" />
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -181,7 +187,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const finded = profile?.permissions?.find(
|
const finded = profile?.permissions?.find(
|
||||||
(item: any) => item.page_name === page
|
(item: any) => item.page_name === page,
|
||||||
);
|
);
|
||||||
if (finded && finded.page_access.includes(access)) {
|
if (finded && finded.page_access.includes(access)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -237,7 +243,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
sizeStyles.padding,
|
sizeStyles.padding,
|
||||||
sizeStyles.text,
|
sizeStyles.text,
|
||||||
className,
|
className,
|
||||||
checkIsMobile() && !icon && !variant && children && mobileBorders
|
checkIsMobile() && !icon && !variant && children && mobileBorders,
|
||||||
)}
|
)}
|
||||||
style={{ height }}
|
style={{ height }}
|
||||||
>
|
>
|
||||||
@@ -256,7 +262,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
closeBackdrop();
|
closeBackdrop();
|
||||||
const url = window.URL.createObjectURL(
|
const url = window.URL.createObjectURL(
|
||||||
new Blob([response.data])
|
new Blob([response.data]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
|
|||||||
@@ -226,17 +226,25 @@ export const FormApiBasedAutoComplete = ({
|
|||||||
setSelectedKeys(defaultItems.map((item: any) => item.key));
|
setSelectedKeys(defaultItems.map((item: any) => item.key));
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
if (secondaryKey) {
|
if (secondaryKey) {
|
||||||
onChange({
|
console.log("dksk0", defaultItems);
|
||||||
key1: defaultItems.map((item: any) => item.key),
|
onChange(
|
||||||
key2: defaultItems.map((item: any) => item.secondaryKey),
|
defaultItems.map((item: any) => {
|
||||||
|
return {
|
||||||
|
key: item?.key,
|
||||||
|
key2: item?.secondaryKey,
|
||||||
...(tertiaryKey
|
...(tertiaryKey
|
||||||
? {
|
? {
|
||||||
key3: defaultItems.map(
|
key3: item?.tertiaryKey,
|
||||||
(item: any) => item.tertiaryKey
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
});
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (onChangeValue) {
|
||||||
|
onChangeValue(
|
||||||
|
defaultItems.map((item: any) => item.value.trim())
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
onChange(defaultItems.map((item: any) => item.key));
|
onChange(defaultItems.map((item: any) => item.key));
|
||||||
}
|
}
|
||||||
|
|||||||
254
src/partials/tagging/DistributeFromDistribution.tsx
Normal file
254
src/partials/tagging/DistributeFromDistribution.tsx
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Grid } from "../../components/Grid/Grid";
|
||||||
|
import Button from "../../components/Button/Button";
|
||||||
|
import Textfield from "../../components/Textfeild/Textfeild";
|
||||||
|
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete";
|
||||||
|
import AutoComplete from "../../components/AutoComplete/AutoComplete";
|
||||||
|
import { zValidateAutoComplete } from "../../data/getFormTypeErrors";
|
||||||
|
import { useApiMutation, useApiRequest } from "../../utils/useApiRequest";
|
||||||
|
import { useToast } from "../../hooks/useToast";
|
||||||
|
import { useModalStore } from "../../context/zustand-store/appStore";
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
organization: zValidateAutoComplete("سازمان"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormValues = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
type ParentDistItem = {
|
||||||
|
id: number;
|
||||||
|
dist_identity?: number;
|
||||||
|
batch_identity: string | number | null;
|
||||||
|
species_code: number;
|
||||||
|
maxCount: number;
|
||||||
|
label?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DistributeFromDistribution = ({ item, getData }: any) => {
|
||||||
|
const showToast = useToast();
|
||||||
|
const { closeModal } = useModalStore();
|
||||||
|
|
||||||
|
const [dists, setDists] = useState<ParentDistItem[]>([]);
|
||||||
|
const [selectedSpeciesKeys, setSelectedSpeciesKeys] = useState<
|
||||||
|
(string | number)[]
|
||||||
|
>([]);
|
||||||
|
const [counts, setCounts] = useState<Record<number, number | "">>({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
trigger,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
organization: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: batchDetail } = useApiRequest({
|
||||||
|
api: `/tag/web/api/v1/tag_distribution_batch/${item?.id}/`,
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagDistributionBatchDetail", item?.id],
|
||||||
|
enabled: !!item?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mutation = useApiMutation({
|
||||||
|
api: `/tag/web/api/v1/tag_distribution/${item?.id}/distribute_distribution/`,
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: speciesData } = useApiRequest({
|
||||||
|
api: "/livestock/web/api/v1/livestock_species",
|
||||||
|
method: "get",
|
||||||
|
params: { page: 1, pageSize: 1000 },
|
||||||
|
queryKey: ["species"],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const sourceDistributions = item?.distributions?.length
|
||||||
|
? item.distributions
|
||||||
|
: batchDetail?.distributions;
|
||||||
|
|
||||||
|
if (!sourceDistributions?.length) {
|
||||||
|
setDists([]);
|
||||||
|
setCounts({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentDists: ParentDistItem[] = sourceDistributions.map((d: any) => {
|
||||||
|
const maxCount = d?.remaining_number || 0;
|
||||||
|
return {
|
||||||
|
id: d?.id ?? d?.dist_identity,
|
||||||
|
dist_identity: d?.dist_identity,
|
||||||
|
batch_identity: d?.batch_identity ?? null,
|
||||||
|
species_code: d?.species_code,
|
||||||
|
maxCount: Number(maxCount) || 0,
|
||||||
|
label:
|
||||||
|
d?.serial_from != null && d?.serial_to != null
|
||||||
|
? `از ${d.serial_from} تا ${d.serial_to}`
|
||||||
|
: undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setDists(parentDists);
|
||||||
|
setCounts({});
|
||||||
|
setSelectedSpeciesKeys([]);
|
||||||
|
}, [item?.distributions, batchDetail?.distributions]);
|
||||||
|
|
||||||
|
const speciesOptions = () =>
|
||||||
|
speciesData?.results?.map((opt: any) => ({
|
||||||
|
key: opt?.value,
|
||||||
|
value: opt?.name,
|
||||||
|
})) ?? [];
|
||||||
|
|
||||||
|
const getSpeciesName = (speciesCode: number) =>
|
||||||
|
speciesOptions().find((s: any) => s.key === speciesCode)?.value ?? "نامشخص";
|
||||||
|
|
||||||
|
const visibleDists = dists.filter((d) =>
|
||||||
|
selectedSpeciesKeys.includes(d.species_code),
|
||||||
|
);
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormValues) => {
|
||||||
|
const distsPayload = visibleDists
|
||||||
|
.filter((d) => {
|
||||||
|
const c = counts[d.id];
|
||||||
|
return c !== "" && c !== undefined && c !== null && Number(c) > 0;
|
||||||
|
})
|
||||||
|
.map((d) => {
|
||||||
|
const fromItem = item?.distributions?.find(
|
||||||
|
(x: any) => x.id === d.id || x.dist_identity === d.id,
|
||||||
|
);
|
||||||
|
const batch_identity =
|
||||||
|
fromItem != null ? fromItem.batch_identity : d.batch_identity;
|
||||||
|
return {
|
||||||
|
parent_tag_distribution: d.id,
|
||||||
|
batch_identity: batch_identity != null ? batch_identity : null,
|
||||||
|
species_code: d.species_code,
|
||||||
|
count: Number(counts[d.id] ?? 0),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if (distsPayload.length === 0) {
|
||||||
|
showToast("حداقل یک گونه با تعداد معتبر وارد کنید", "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mutation.mutateAsync({
|
||||||
|
assigned_org: data.organization[0],
|
||||||
|
parent_distribution_batch: item.id,
|
||||||
|
dists: distsPayload,
|
||||||
|
});
|
||||||
|
|
||||||
|
showToast("توزیع از توزیع با موفقیت انجام شد", "success");
|
||||||
|
getData();
|
||||||
|
closeModal();
|
||||||
|
} catch (error: any) {
|
||||||
|
showToast(
|
||||||
|
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||||
|
"error",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCountChange = (distId: number, value: number | "") => {
|
||||||
|
setCounts((prev) => ({ ...prev, [distId]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidCount = (dist: ParentDistItem) => {
|
||||||
|
const c = counts[dist.id];
|
||||||
|
if (c === "" || c === undefined || c === null) return false;
|
||||||
|
const num = Number(c);
|
||||||
|
return num > 0 && num <= dist.maxCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
const speciesOptionsFromParent = () => {
|
||||||
|
const uniqueSpecies = Array.from(
|
||||||
|
new Map(dists.map((d) => [d.species_code, d])).values(),
|
||||||
|
);
|
||||||
|
return uniqueSpecies.map((d) => ({
|
||||||
|
key: d.species_code,
|
||||||
|
value: getSpeciesName(d.species_code),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasValidDists =
|
||||||
|
visibleDists.length > 0 && visibleDists.every((d) => isValidCount(d));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<Grid container column className="gap-3">
|
||||||
|
<Controller
|
||||||
|
name="organization"
|
||||||
|
control={control}
|
||||||
|
render={() => (
|
||||||
|
<FormApiBasedAutoComplete
|
||||||
|
title="انتخاب سازمان (دریافتکننده)"
|
||||||
|
api={`auth/api/v1/organization/organizations_by_province?exclude=PSP&province=${item?.assigner_org?.province}`}
|
||||||
|
keyField="id"
|
||||||
|
valueField="name"
|
||||||
|
error={!!errors.organization}
|
||||||
|
errorMessage={errors.organization?.message}
|
||||||
|
onChange={(r) => {
|
||||||
|
setValue("organization", [r]);
|
||||||
|
trigger("organization");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{dists.length > 0 && speciesData?.results && (
|
||||||
|
<>
|
||||||
|
<AutoComplete
|
||||||
|
data={speciesOptionsFromParent()}
|
||||||
|
multiselect
|
||||||
|
selectedKeys={selectedSpeciesKeys}
|
||||||
|
onChange={(keys: (string | number)[]) => {
|
||||||
|
setSelectedSpeciesKeys(keys);
|
||||||
|
}}
|
||||||
|
title="گونه"
|
||||||
|
/>
|
||||||
|
{visibleDists.map((dist) => {
|
||||||
|
const countVal = counts[dist.id];
|
||||||
|
const numCount =
|
||||||
|
countVal !== "" && countVal !== undefined && countVal !== null
|
||||||
|
? Number(countVal)
|
||||||
|
: null;
|
||||||
|
const isOverMax = numCount !== null && numCount > dist.maxCount;
|
||||||
|
const isEmpty = countVal === "" || countVal === undefined;
|
||||||
|
const helperText = isOverMax
|
||||||
|
? `تعداد نباید بیشتر از ${dist.maxCount.toLocaleString()} باشد`
|
||||||
|
: isEmpty
|
||||||
|
? "لطفا تعداد را وارد کنید"
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Textfield
|
||||||
|
key={dist.id}
|
||||||
|
fullWidth
|
||||||
|
formattedNumber
|
||||||
|
placeholder={`تعداد ${getSpeciesName(dist.species_code)}${dist.label ? ` (${dist.label})` : ""} — حداکثر: ${dist.maxCount.toLocaleString()}`}
|
||||||
|
value={counts[dist.id] ?? ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleCountChange(dist.id, Number(e.target.value))
|
||||||
|
}
|
||||||
|
error={isOverMax}
|
||||||
|
helperText={helperText}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button disabled={!hasValidDists} type="submit">
|
||||||
|
ثبت
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
54
src/partials/tagging/DistributionSpeciesModal.tsx
Normal file
54
src/partials/tagging/DistributionSpeciesModal.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
export const DistributionSpeciesModal = ({ items }: { items: any[] }) => {
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
|
{items?.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="rounded-xl border border-gray-200 dark:border-gray-700 p-4
|
||||||
|
bg-white dark:bg-gray-800
|
||||||
|
hover:shadow-sm transition"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<span className="text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||||
|
گونه
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-semibold text-gray-800 dark:text-gray-100">
|
||||||
|
{speciesMap[item?.species_code] ?? "-"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-px bg-gray-100 dark:bg-gray-700 my-2" />
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
تعداد توزیع
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||||
|
{item?.dist_count?.toLocaleString() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
|
تعداد پلاک
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||||
|
{item?.tag_count?.toLocaleString() ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -45,8 +45,6 @@ export const SubmitNewTags = ({ getData, item }: SubmitNewTagsTypeProps) => {
|
|||||||
|
|
||||||
const { profile } = useUserProfileStore();
|
const { profile } = useUserProfileStore();
|
||||||
|
|
||||||
console.log(item);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -58,13 +56,15 @@ export const SubmitNewTags = ({ getData, item }: SubmitNewTagsTypeProps) => {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
country_code: 364,
|
country_code: 364,
|
||||||
static_code: 0,
|
static_code: 0,
|
||||||
species_code: 1,
|
serial_start: item?.serial_from || "",
|
||||||
|
serial_end: item?.serial_to || "",
|
||||||
|
species_code: item?.species_code || 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mutation = useApiMutation({
|
const mutation = useApiMutation({
|
||||||
api: "/tag/web/api/v1/tag/",
|
api: `/tag/web/api/v1/tag/${item?.id ? item.id : ""}`,
|
||||||
method: "post",
|
method: item ? "put" : "post",
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async (data: FormValues) => {
|
const onSubmit = async (data: FormValues) => {
|
||||||
|
|||||||
284
src/partials/tagging/SubmitTagDistribution.tsx
Normal file
284
src/partials/tagging/SubmitTagDistribution.tsx
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { Grid } from "../../components/Grid/Grid";
|
||||||
|
import Button from "../../components/Button/Button";
|
||||||
|
import Textfield from "../../components/Textfeild/Textfeild";
|
||||||
|
import { RadioGroup } from "../../components/RadioButton/RadioGroup";
|
||||||
|
import { FormApiBasedAutoComplete } from "../../components/FormItems/FormApiBasedAutoComplete";
|
||||||
|
import AutoComplete from "../../components/AutoComplete/AutoComplete";
|
||||||
|
import { zValidateAutoComplete } from "../../data/getFormTypeErrors";
|
||||||
|
import { useApiMutation, useApiRequest } from "../../utils/useApiRequest";
|
||||||
|
import { useToast } from "../../hooks/useToast";
|
||||||
|
import { useModalStore } from "../../context/zustand-store/appStore";
|
||||||
|
|
||||||
|
const distributionTypeOptions = [
|
||||||
|
{ label: "توزیع گروهی", value: "group" },
|
||||||
|
{ label: "توزیع تصادفی", value: "random" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
organization: zValidateAutoComplete("سازمان"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormValues = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
type BatchItem = {
|
||||||
|
batch_identity?: string | number;
|
||||||
|
species_code?: number;
|
||||||
|
count: number | "";
|
||||||
|
label?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SubmitTagDistribution = ({ item, getData }: any) => {
|
||||||
|
const showToast = useToast();
|
||||||
|
const { closeModal } = useModalStore();
|
||||||
|
|
||||||
|
const isEdit = Boolean(item?.id);
|
||||||
|
|
||||||
|
const [distributionType, setDistributionType] = useState<"group" | "random">(
|
||||||
|
isEdit
|
||||||
|
? item?.distribution_type === "random"
|
||||||
|
? "random"
|
||||||
|
: "group"
|
||||||
|
: "group",
|
||||||
|
);
|
||||||
|
const [batches, setBatches] = useState<BatchItem[]>([]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
setValue,
|
||||||
|
trigger,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
organization: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mutation = useApiMutation({
|
||||||
|
api: isEdit
|
||||||
|
? `/tag/web/api/v1/tag_distribution/${item?.id}`
|
||||||
|
: "/tag/web/api/v1/tag_distribution/",
|
||||||
|
method: isEdit ? "put" : "post",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: speciesData } = useApiRequest({
|
||||||
|
api: "/livestock/web/api/v1/livestock_species",
|
||||||
|
method: "get",
|
||||||
|
params: { page: 1, pageSize: 1000 },
|
||||||
|
queryKey: ["species"],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
setValue("organization", [item.assigned_org?.id]);
|
||||||
|
trigger("organization");
|
||||||
|
|
||||||
|
const mappedBatches = item.distributions.map((d: any) => ({
|
||||||
|
...(item.distribution_type === "batch" && {
|
||||||
|
batch_identity: item.dist_batch_identity,
|
||||||
|
}),
|
||||||
|
species_code: d.species_code,
|
||||||
|
count: d.distributed_number,
|
||||||
|
label:
|
||||||
|
item.distribution_type === "batch"
|
||||||
|
? `از ${d.serial_from ?? "-"} تا ${d.serial_to ?? "-"}`
|
||||||
|
: undefined,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setBatches(mappedBatches);
|
||||||
|
}, [item]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormValues) => {
|
||||||
|
const dists =
|
||||||
|
distributionType === "random"
|
||||||
|
? batches.map((b) => ({
|
||||||
|
species_code: b.species_code,
|
||||||
|
count: b.count,
|
||||||
|
}))
|
||||||
|
: batches.map((b) => ({
|
||||||
|
batch_identity: b.batch_identity,
|
||||||
|
species_code: b.species_code,
|
||||||
|
count: b.count,
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mutation.mutateAsync({
|
||||||
|
assigner_org: item?.organization?.id,
|
||||||
|
assigned_org: data.organization[0],
|
||||||
|
dists,
|
||||||
|
});
|
||||||
|
|
||||||
|
showToast(
|
||||||
|
isEdit ? "ویرایش با موفقیت انجام شد" : "ثبت با موفقیت انجام شد",
|
||||||
|
"success",
|
||||||
|
);
|
||||||
|
getData();
|
||||||
|
closeModal();
|
||||||
|
} catch (error: any) {
|
||||||
|
showToast(
|
||||||
|
error?.response?.data?.message || "خطا در ثبت اطلاعات!",
|
||||||
|
"error",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const speciesOptions = () => {
|
||||||
|
return (
|
||||||
|
speciesData?.results?.map((opt: any) => ({
|
||||||
|
key: opt?.value,
|
||||||
|
value: opt?.name,
|
||||||
|
})) ?? []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<Grid container column className="gap-3">
|
||||||
|
<Controller
|
||||||
|
name="organization"
|
||||||
|
control={control}
|
||||||
|
render={() => (
|
||||||
|
<FormApiBasedAutoComplete
|
||||||
|
defaultKey={item?.assigned_org?.id}
|
||||||
|
title="انتخاب سازمان"
|
||||||
|
api={`auth/api/v1/organization/organizations_by_province?exclude=PSP&province=${item?.assigner_org?.province}`}
|
||||||
|
keyField="id"
|
||||||
|
valueField="name"
|
||||||
|
error={!!errors.organization}
|
||||||
|
errorMessage={errors.organization?.message}
|
||||||
|
onChange={(r) => {
|
||||||
|
setValue("organization", [r]);
|
||||||
|
trigger("organization");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
direction="row"
|
||||||
|
options={distributionTypeOptions}
|
||||||
|
value={distributionType}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value as "group" | "random";
|
||||||
|
setDistributionType(val);
|
||||||
|
setBatches([]);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{distributionType === "group" && (
|
||||||
|
<FormApiBasedAutoComplete
|
||||||
|
title="گروه پلاک"
|
||||||
|
api="/tag/web/api/v1/tag_batch/"
|
||||||
|
keyField="batch_identity"
|
||||||
|
secondaryKey="species_code"
|
||||||
|
valueTemplate="از v1 تا v2"
|
||||||
|
valueField={["serial_from"]}
|
||||||
|
valueField2={["serial_to"]}
|
||||||
|
groupBy="species_code"
|
||||||
|
defaultKey={
|
||||||
|
item?.distributions?.map((d: any) => d.batch_identity) || []
|
||||||
|
}
|
||||||
|
groupFunction={(item) =>
|
||||||
|
speciesOptions().find((s: any) => s.key === item)?.value ||
|
||||||
|
"نامشخص"
|
||||||
|
}
|
||||||
|
valueTemplateProps={[{ v1: "string" }, { v2: "string" }]}
|
||||||
|
multiple
|
||||||
|
onChange={(items) => {
|
||||||
|
setBatches(
|
||||||
|
items?.map((r: any) => {
|
||||||
|
const existing = batches.find(
|
||||||
|
(b) =>
|
||||||
|
b.batch_identity === r.key1 && b.species_code === r.key2,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
batch_identity: r.key1,
|
||||||
|
species_code: r.key2,
|
||||||
|
count: existing?.count ?? "",
|
||||||
|
};
|
||||||
|
}) || [],
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onChangeValue={(labels) => {
|
||||||
|
setBatches((prev) =>
|
||||||
|
prev.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
label: labels[index],
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{distributionType === "random" && speciesData?.results && (
|
||||||
|
<AutoComplete
|
||||||
|
data={speciesOptions()}
|
||||||
|
multiselect
|
||||||
|
selectedKeys={batches.map((b) => b.species_code)}
|
||||||
|
onChange={(keys: (string | number)[]) => {
|
||||||
|
setBatches(
|
||||||
|
keys.map((k) => {
|
||||||
|
const prev = batches.find((b) => b.species_code === k);
|
||||||
|
return {
|
||||||
|
species_code: k as number,
|
||||||
|
count: prev?.count ?? "",
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
title="گونه"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{batches.map((batch, index) => (
|
||||||
|
<Textfield
|
||||||
|
key={index}
|
||||||
|
fullWidth
|
||||||
|
formattedNumber
|
||||||
|
placeholder={
|
||||||
|
distributionType === "group"
|
||||||
|
? `تعداد ${
|
||||||
|
speciesOptions().find(
|
||||||
|
(s: any) => s.key === batch.species_code,
|
||||||
|
)?.value
|
||||||
|
} (${batch.label}) `
|
||||||
|
: `تعداد ${
|
||||||
|
speciesOptions().find(
|
||||||
|
(s: any) => s.key === batch.species_code,
|
||||||
|
)?.value
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
value={batch.count}
|
||||||
|
onChange={(e) => {
|
||||||
|
const next = [...batches];
|
||||||
|
next[index].count = Number(e.target.value);
|
||||||
|
setBatches(next);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={
|
||||||
|
batches.length === 0 ||
|
||||||
|
batches.some(
|
||||||
|
(b) =>
|
||||||
|
b.count === "" ||
|
||||||
|
b.count === undefined ||
|
||||||
|
b.count === null ||
|
||||||
|
Number(b.count) <= 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{isEdit ? "ویرایش" : "ثبت"}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
330
src/partials/tagging/TagActiveDistributions.tsx
Normal file
330
src/partials/tagging/TagActiveDistributions.tsx
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
Bars3Icon,
|
||||||
|
CubeIcon,
|
||||||
|
SparklesIcon,
|
||||||
|
StopCircleIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
import { useModalStore } from "../../context/zustand-store/appStore";
|
||||||
|
import { useApiRequest } from "../../utils/useApiRequest";
|
||||||
|
import { formatJustDate, formatJustTime } from "../../utils/formatTime";
|
||||||
|
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
|
||||||
|
import { Grid } from "../../components/Grid/Grid";
|
||||||
|
import Typography from "../../components/Typography/Typography";
|
||||||
|
import { Popover } from "../../components/PopOver/PopOver";
|
||||||
|
import Button from "../../components/Button/Button";
|
||||||
|
import { Tooltip } from "../../components/Tooltip/Tooltip";
|
||||||
|
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
|
||||||
|
import { SubmitTagDistribution } from "./SubmitTagDistribution";
|
||||||
|
import { DistributeFromDistribution } from "./DistributeFromDistribution";
|
||||||
|
import Table from "../../components/Table/Table";
|
||||||
|
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion";
|
||||||
|
import { TableButton } from "../../components/TableButton/TableButton";
|
||||||
|
import { DistributionSpeciesModal } from "./DistributionSpeciesModal";
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
import { TAG_DISTRIBUTION } from "../../routes/paths";
|
||||||
|
|
||||||
|
export default function TagActiveDistributions() {
|
||||||
|
const { openModal } = useModalStore();
|
||||||
|
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
||||||
|
const [tagsTableData, setTagsTableData] = useState([]);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const { data: tagsData, refetch } = useApiRequest({
|
||||||
|
api: "/tag/web/api/v1/tag_distribution_batch",
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagsList", tableInfo],
|
||||||
|
params: {
|
||||||
|
...tableInfo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: tagDashboardData, refetch: updateDashboard } = useApiRequest({
|
||||||
|
api: "/tag/web/api/v1/tag_distribution_batch/main_dashboard/?is_closed=false",
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagDistributionActivesDashboard"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleUpdate = () => {
|
||||||
|
refetch();
|
||||||
|
updateDashboard();
|
||||||
|
};
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tagsData?.results) {
|
||||||
|
const formattedData = tagsData.results.map((item: any, index: number) => {
|
||||||
|
const dist = item?.distributions;
|
||||||
|
|
||||||
|
return [
|
||||||
|
tableInfo.page === 1
|
||||||
|
? index + 1
|
||||||
|
: index + tableInfo.page_size * (tableInfo.page - 1) + 1,
|
||||||
|
item?.dist_batch_identity,
|
||||||
|
`${formatJustDate(item?.create_date)} (${
|
||||||
|
formatJustDate(item?.create_date)
|
||||||
|
? formatJustTime(item?.create_date)
|
||||||
|
: "-"
|
||||||
|
})`,
|
||||||
|
item?.assigner_org?.name,
|
||||||
|
item?.assigned_org?.name,
|
||||||
|
item?.total_tag_count,
|
||||||
|
item?.total_distributed_tag_count,
|
||||||
|
item?.remaining_tag_count,
|
||||||
|
item?.distribution_type === "batch" ? "توزیع گروهی" : "توزیع تصادفی",
|
||||||
|
<ShowMoreInfo key={item?.id} title="جزئیات توزیع">
|
||||||
|
<Grid container column className="gap-4 w-full">
|
||||||
|
{dist?.map((opt: any, index: number) => (
|
||||||
|
<Grid
|
||||||
|
key={index}
|
||||||
|
container
|
||||||
|
column
|
||||||
|
className="gap-3 w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4"
|
||||||
|
>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<SparklesIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
گونه:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{speciesMap[opt?.species_code] ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{item?.distribution_type === "batch" && opt?.serial_from && (
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<Bars3Icon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
بازه سریال:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-600 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
از {opt?.serial_from ?? "-"} تا {opt?.serial_to ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
تعداد پلاک:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.total_tag_count?.toLocaleString()}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
پلاک های توزیع شده:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.distributed_number?.toLocaleString()}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
پلاک های باقیمانده:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.remaining_number?.toLocaleString()}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</ShowMoreInfo>,
|
||||||
|
<Popover key={index}>
|
||||||
|
<Tooltip title="جزيٓیات توزیع" position="right">
|
||||||
|
<Button
|
||||||
|
variant="detail"
|
||||||
|
page="tag_distribution_detail"
|
||||||
|
access="Show-Tag-Distribution-Detail"
|
||||||
|
onClick={() => {
|
||||||
|
const path =
|
||||||
|
TAG_DISTRIBUTION +
|
||||||
|
"/" +
|
||||||
|
item?.dist_batch_identity +
|
||||||
|
"/" +
|
||||||
|
item?.id;
|
||||||
|
navigate({ to: path });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="ویرایش توزیع" position="right">
|
||||||
|
<Button
|
||||||
|
variant="edit"
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Submit-Tag-Distribution"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "ویرایش توزیع پلاک",
|
||||||
|
content: (
|
||||||
|
<SubmitTagDistribution
|
||||||
|
getData={handleUpdate}
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="توزیع مجدد" position="right">
|
||||||
|
<Button
|
||||||
|
variant="share"
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Distribute-From-Distribution"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "توزیع مجدد",
|
||||||
|
content: (
|
||||||
|
<DistributeFromDistribution
|
||||||
|
getData={handleUpdate}
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={"لغو توزیع"} position="right">
|
||||||
|
<Button
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Cancel-Tag-Distribution"
|
||||||
|
icon={<StopCircleIcon className="w-5 h-5 text-red-400" />}
|
||||||
|
variant="set"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "لغو توزیع پلاک",
|
||||||
|
content: (
|
||||||
|
<BooleanQuestion
|
||||||
|
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/close_dist_batch/`}
|
||||||
|
method="post"
|
||||||
|
getData={handleUpdate}
|
||||||
|
title="آیا از لغو توزیع پلاک مطمئنید؟"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<DeleteButtonForPopOver
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Delete-Tag-Distribution"
|
||||||
|
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/`}
|
||||||
|
getData={refetch}
|
||||||
|
/>
|
||||||
|
</Popover>,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
setTagsTableData(formattedData);
|
||||||
|
} else {
|
||||||
|
setTagsTableData([]);
|
||||||
|
}
|
||||||
|
}, [tagsData, tableInfo]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container column className="gap-4 mt-2">
|
||||||
|
<Grid>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="submit"
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Submit-Tag-Distribution"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "توزیع پلاک",
|
||||||
|
content: <SubmitTagDistribution getData={handleUpdate} />,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
توزیع پلاک
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid isDashboard>
|
||||||
|
<Table
|
||||||
|
isDashboard
|
||||||
|
title="خلاصه اطلاعات"
|
||||||
|
noPagination
|
||||||
|
noSearch
|
||||||
|
columns={[
|
||||||
|
"تعداد توزیع",
|
||||||
|
"پلاک های ارسالی",
|
||||||
|
"پلاک های دریافتی",
|
||||||
|
"توزیع های دریافتی",
|
||||||
|
"توزیع های ارسالی",
|
||||||
|
"جزئیات",
|
||||||
|
]}
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
tagDashboardData?.count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_sent_tag_count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_recieved_tag_count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_recieved_distributions?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
tagDashboardData?.total_sent_distributions?.toLocaleString() || 0,
|
||||||
|
<TableButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "جزئیات",
|
||||||
|
content: (
|
||||||
|
<DistributionSpeciesModal
|
||||||
|
items={tagDashboardData?.items}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
className="mt-2"
|
||||||
|
onChange={setTableInfo}
|
||||||
|
count={tagsData?.count || 0}
|
||||||
|
isPaginated
|
||||||
|
title="توزیع پلاک"
|
||||||
|
columns={[
|
||||||
|
"ردیف",
|
||||||
|
"شناسه توزیع",
|
||||||
|
"تاریخ ثبت",
|
||||||
|
"توزیع کننده",
|
||||||
|
"دریافت کننده",
|
||||||
|
"تعداد کل پلاک",
|
||||||
|
"پلاک های توزیع شده",
|
||||||
|
"پلاک های باقیمانده",
|
||||||
|
"نوع توزیع",
|
||||||
|
"جزئیات توزیع",
|
||||||
|
"عملیات",
|
||||||
|
]}
|
||||||
|
rows={tagsTableData}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
228
src/partials/tagging/TagCanceledDistributions.tsx
Normal file
228
src/partials/tagging/TagCanceledDistributions.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
BackwardIcon,
|
||||||
|
Bars3Icon,
|
||||||
|
CubeIcon,
|
||||||
|
SparklesIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
import { useModalStore } from "../../context/zustand-store/appStore";
|
||||||
|
import { useApiRequest } from "../../utils/useApiRequest";
|
||||||
|
import { formatJustDate, formatJustTime } from "../../utils/formatTime";
|
||||||
|
import ShowMoreInfo from "../../components/ShowMoreInfo/ShowMoreInfo";
|
||||||
|
import { Grid } from "../../components/Grid/Grid";
|
||||||
|
import Typography from "../../components/Typography/Typography";
|
||||||
|
import { Popover } from "../../components/PopOver/PopOver";
|
||||||
|
import Button from "../../components/Button/Button";
|
||||||
|
import { Tooltip } from "../../components/Tooltip/Tooltip";
|
||||||
|
import { DeleteButtonForPopOver } from "../../components/PopOverButtons/PopOverButtons";
|
||||||
|
|
||||||
|
import Table from "../../components/Table/Table";
|
||||||
|
import { BooleanQuestion } from "../../components/BooleanQuestion/BooleanQuestion";
|
||||||
|
import { TableButton } from "../../components/TableButton/TableButton";
|
||||||
|
import { DistributionSpeciesModal } from "./DistributionSpeciesModal";
|
||||||
|
|
||||||
|
export default function TagCanceledDistributions() {
|
||||||
|
const { openModal } = useModalStore();
|
||||||
|
const [tableInfo, setTableInfo] = useState({ page: 1, page_size: 10 });
|
||||||
|
const [tagsTableData, setTagsTableData] = useState([]);
|
||||||
|
|
||||||
|
const { data: tagsData, refetch } = useApiRequest({
|
||||||
|
api: "/tag/web/api/v1/tag_distribution_batch/closed_tag_dist_batch_list",
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagsList", tableInfo],
|
||||||
|
params: {
|
||||||
|
...tableInfo,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: tagDashboardData, refetch: updateDashboard } = useApiRequest({
|
||||||
|
api: "/tag/web/api/v1/tag_distribution_batch/main_dashboard/?is_closed=true",
|
||||||
|
method: "get",
|
||||||
|
queryKey: ["tagDistributionCanceledDashboard"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleUpdate = () => {
|
||||||
|
refetch();
|
||||||
|
updateDashboard();
|
||||||
|
};
|
||||||
|
const speciesMap: Record<number, string> = {
|
||||||
|
1: "گاو",
|
||||||
|
2: "گاومیش",
|
||||||
|
3: "شتر",
|
||||||
|
4: "گوسفند",
|
||||||
|
5: "بز",
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tagsData?.results) {
|
||||||
|
const formattedData = tagsData.results.map((item: any, index: number) => {
|
||||||
|
const dist = item?.distributions;
|
||||||
|
|
||||||
|
return [
|
||||||
|
tableInfo.page === 1
|
||||||
|
? index + 1
|
||||||
|
: index + tableInfo.page_size * (tableInfo.page - 1) + 1,
|
||||||
|
item?.dist_batch_identity,
|
||||||
|
`${formatJustDate(item?.create_date)} (${
|
||||||
|
formatJustDate(item?.create_date)
|
||||||
|
? formatJustTime(item?.create_date)
|
||||||
|
: "-"
|
||||||
|
})`,
|
||||||
|
item?.assigner_org?.name,
|
||||||
|
item?.assigned_org?.name,
|
||||||
|
item?.total_tag_count,
|
||||||
|
item?.distribution_type === "batch" ? "توزیع گروهی" : "توزیع تصادفی",
|
||||||
|
<ShowMoreInfo key={item?.id} title="جزئیات توزیع">
|
||||||
|
<Grid container column className="gap-4 w-full">
|
||||||
|
{dist?.map((opt: any, index: number) => (
|
||||||
|
<Grid
|
||||||
|
key={index}
|
||||||
|
container
|
||||||
|
column
|
||||||
|
className="gap-3 w-full rounded-xl border border-gray-200 dark:border-gray-700 p-4"
|
||||||
|
>
|
||||||
|
{item?.distribution_type === "batch" && opt?.serial_from && (
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<Bars3Icon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
بازه سریال:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-600 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
از {opt?.serial_from ?? "-"} تا {opt?.serial_to ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<CubeIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
تعداد پلاک:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{opt?.distributed_number?.toLocaleString()}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid container className="gap-2 items-center">
|
||||||
|
<SparklesIcon className="w-5 h-5 text-gray-500 dark:text-gray-300" />
|
||||||
|
<Typography variant="body2" className="font-medium">
|
||||||
|
گونه:
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
className="text-gray-700 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
{speciesMap[opt?.species_code] ?? "-"}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</ShowMoreInfo>,
|
||||||
|
<Popover key={index}>
|
||||||
|
<Tooltip title={"برگشت توزیع"} position="right">
|
||||||
|
<Button
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Cancel-Tag-Distribution"
|
||||||
|
icon={<BackwardIcon className="w-5 h-5 text-red-400" />}
|
||||||
|
variant="set"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "برگشت توزیع لغو شده",
|
||||||
|
content: (
|
||||||
|
<BooleanQuestion
|
||||||
|
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/reactivate_tag_dist_batch/`}
|
||||||
|
method="post"
|
||||||
|
getData={handleUpdate}
|
||||||
|
title="آیا از برگشت توزیع پلاک لغو شده مطمئنید؟"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<DeleteButtonForPopOver
|
||||||
|
page="tag_distribution"
|
||||||
|
access="Delete-Tag-Distribution"
|
||||||
|
api={`/tag/web/api/v1/tag_distribution_batch/${item?.id}/`}
|
||||||
|
getData={handleUpdate}
|
||||||
|
/>
|
||||||
|
</Popover>,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
setTagsTableData(formattedData);
|
||||||
|
} else {
|
||||||
|
setTagsTableData([]);
|
||||||
|
}
|
||||||
|
}, [tagsData, tableInfo]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container column className="gap-4 mt-2">
|
||||||
|
<Grid isDashboard>
|
||||||
|
<Table
|
||||||
|
isDashboard
|
||||||
|
title="خلاصه اطلاعات"
|
||||||
|
noPagination
|
||||||
|
noSearch
|
||||||
|
columns={[
|
||||||
|
"تعداد توزیع",
|
||||||
|
"پلاک های ارسالی",
|
||||||
|
"پلاک های دریافتی",
|
||||||
|
"توزیع های دریافتی",
|
||||||
|
"توزیع های ارسالی",
|
||||||
|
"جزئیات",
|
||||||
|
]}
|
||||||
|
rows={[
|
||||||
|
[
|
||||||
|
tagDashboardData?.count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_sent_tag_count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_recieved_tag_count?.toLocaleString() || 0,
|
||||||
|
tagDashboardData?.total_recieved_distributions?.toLocaleString() ||
|
||||||
|
0,
|
||||||
|
tagDashboardData?.total_sent_distributions?.toLocaleString() || 0,
|
||||||
|
<TableButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
openModal({
|
||||||
|
title: "جزئیات",
|
||||||
|
content: (
|
||||||
|
<DistributionSpeciesModal
|
||||||
|
items={tagDashboardData?.items}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Table
|
||||||
|
className="mt-2"
|
||||||
|
onChange={setTableInfo}
|
||||||
|
count={tagsData?.count || 0}
|
||||||
|
isPaginated
|
||||||
|
title="توزیع های لغو شده"
|
||||||
|
columns={[
|
||||||
|
"ردیف",
|
||||||
|
"شناسه توزیع",
|
||||||
|
"تاریخ ثبت",
|
||||||
|
"توزیع کننده",
|
||||||
|
"دریافت کننده",
|
||||||
|
"تعداد کل پلاک",
|
||||||
|
"نوع توزیع",
|
||||||
|
"جزئیات توزیع",
|
||||||
|
"عملیات",
|
||||||
|
]}
|
||||||
|
rows={tagsTableData}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -53,4 +53,6 @@ export const UNITS_SETTINGS = "/unit-settings";
|
|||||||
//TAGGING
|
//TAGGING
|
||||||
export const TAGGING = "/tagging";
|
export const TAGGING = "/tagging";
|
||||||
export const TAGS = "/tags";
|
export const TAGS = "/tags";
|
||||||
|
export const TAG_DISTRIBUTION = "/tag-distribution";
|
||||||
|
export const TAG_DISTRIBUTION_DETAIL = "/tag-distribution/$identity/$id";
|
||||||
export const TAGS_BATCH = "/tags/$id/$from/$to";
|
export const TAGS_BATCH = "/tags/$id/$from/$to";
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ export const formatJustDate = (time: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const formatJustTime = (time: any) => {
|
export const formatJustTime = (time: any) => {
|
||||||
|
if (time) {
|
||||||
return format(new Date(time), "HH:mm");
|
return format(new Date(time), "HH:mm");
|
||||||
|
} else return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
export function formatStampDate(timestamp: number) {
|
export function formatStampDate(timestamp: number) {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import CooperativeRanchers from "../Pages/CooperativeRanchers";
|
|||||||
import SettingsOfUnits from "../Pages/SettingsOfUnits";
|
import SettingsOfUnits from "../Pages/SettingsOfUnits";
|
||||||
import Tagging from "../Pages/Tagging";
|
import Tagging from "../Pages/Tagging";
|
||||||
import Tags from "../Pages/Tags";
|
import Tags from "../Pages/Tags";
|
||||||
|
import TagDistribtution from "../Pages/TagDistribution";
|
||||||
|
import TagDistribtutionDetails from "../Pages/TagDistributionDetails";
|
||||||
|
|
||||||
export const managementCategoryItems = [
|
export const managementCategoryItems = [
|
||||||
{
|
{
|
||||||
@@ -182,6 +184,16 @@ export const taggingCategoryItems = [
|
|||||||
path: R.TAGS_BATCH,
|
path: R.TAGS_BATCH,
|
||||||
component: Tags,
|
component: Tags,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "tag_distribution",
|
||||||
|
path: R.TAG_DISTRIBUTION,
|
||||||
|
component: TagDistribtution,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tag_distribution_detail",
|
||||||
|
path: R.TAG_DISTRIBUTION_DETAIL,
|
||||||
|
component: TagDistribtutionDetails,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const posCategoryItems = [
|
export const posCategoryItems = [
|
||||||
|
|||||||
@@ -100,6 +100,12 @@ export function getFaPermissions(permission: string) {
|
|||||||
case "tags":
|
case "tags":
|
||||||
faPermission = "پلاک ها";
|
faPermission = "پلاک ها";
|
||||||
break;
|
break;
|
||||||
|
case "tag_distribution":
|
||||||
|
faPermission = "توزیع پلاک";
|
||||||
|
break;
|
||||||
|
case "tag_distribution_detail":
|
||||||
|
faPermission = "جزئیات توزیع پلاک";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
02.52
|
02.64
|
||||||
|
|||||||
Reference in New Issue
Block a user