push rasad front on new repo

This commit is contained in:
2026-01-18 14:32:49 +03:30
commit 4fe6e70525
2139 changed files with 303150 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import { MdCorporateFare } from "react-icons/md";
import { Grid } from "../../../../components/grid/Grid";
import LinkItem from "../../../../components/link-item/LinkItem";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { SPACING } from "../../../../data/spacing";
import {
ROUTE_ADMINX_ROUTE_AGENT_SHARE,
ROUTE_ADMINX_ROUTE_STEWARD_SHARE,
ROUTE_PROVINCE_ROUTE_AGENT_SHARE,
ROUTE_PROVINCE_ROUTE_STEWARD_SHARE,
ROUTE_SUPER_ADMIN_ROUTE_AGENT_SHARE,
ROUTE_SUPER_ADMIN_ROUTE_STEWARD_SHARE,
} from "../../../../routes/routes";
import { Typography } from "@mui/material";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
const getRouteByRole = (isAgentRoute) => {
const role = getRoleFromUrl();
if (role === "SuperAdmin") {
if (isAgentRoute) {
return ROUTE_SUPER_ADMIN_ROUTE_AGENT_SHARE;
}
return ROUTE_SUPER_ADMIN_ROUTE_STEWARD_SHARE;
}
if (role === "AdminX") {
if (isAgentRoute) {
return ROUTE_ADMINX_ROUTE_AGENT_SHARE;
}
return ROUTE_ADMINX_ROUTE_STEWARD_SHARE;
}
if (isAgentRoute) {
return ROUTE_PROVINCE_ROUTE_AGENT_SHARE;
}
return ROUTE_PROVINCE_ROUTE_STEWARD_SHARE;
};
export const BroadcastManagementOperations = () => {
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
>
<NavLink to={getRouteByRole(true)}>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title={
<>
<Typography>گزارش پخش روزانه</Typography>
<Typography variant="caption">کشتارگاه به مباشر/ صنف</Typography>
</>
}
/>
</NavLink>
<NavLink to={getRouteByRole(false)}>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title={
<>
<Typography>گزارش پخش روزانه</Typography>
<Typography variant="caption">مباشر به صنف</Typography>
</>
}
/>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,264 @@
import { Button, TextField, Tooltip, Typography } from "@mui/material";
import axios from "axios";
import { useEffect, useState } from "react";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { CityHatchingsArchiveOperations } from "../../../city/components/city-hatchings-archive-operations/CityHatchingsArchiveOperations";
import { formatTime } from "../../../../utils/formatTime";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { useSelector } from "react-redux";
const OperationsCell = ({ item, updateArchive, readOnly }) => (
<CityHatchingsArchiveOperations
item={item}
updateArchive={updateArchive}
readOnly={readOnly}
/>
);
export const ChainsArchiveChains = ({ readOnly }) => {
readOnly = readOnly || false;
const [data, setData] = useState([]);
const [tableData, setTableData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [page, setPage] = useState(1);
const [textValue, setTextValue] = useState("");
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const fetchApiData = async (page, textValue) => {
let response;
if (textValue) {
response = await axios.get(
`poultry_hatching/?archive=true&search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=&chain=true`
);
} else {
response = await axios.get(
`poultry_hatching/?archive=true&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&chain=true`
);
}
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (pageNum) => {
setPage(pageNum);
fetchApiData(pageNum, textValue);
};
const handlePerRowsChange = (newPerPage) => {
setPerPage(Number(newPerPage));
setPage(1);
};
useEffect(() => {
if (perPage) {
fetchApiData(1, textValue);
}
}, [perPage]);
useEffect(() => {
fetchApiData(1, "");
}, []);
const columns = [
"عملیات",
"ردیف",
"شرکت زنجیره",
"وضعیت",
"نام فارم",
"مرغدار",
"شهر/تعاونی",
"دامپزشک فارم",
"سالن",
"دوره جوجه ریزی",
"تاریخ ثبت جوجه ریزی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"تلفات دوره",
"حجم خارج از استان",
"وزن کشتار شده دولتی",
"وزن کشتار شده آزاد ",
"وزن خارج از استان",
"حخم کشتار زنجیره (قطعه)",
"وزن کشتار زنجیره (کیلوگرم)",
"کشتار شده",
"مانده در سالن",
"کشتار فعال",
"تعداد درخواست کشتار",
"آخرین تغییر",
];
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const transformedData = data.map((item, i) => {
const killedNumber = item?.quantity - item?.losses - item?.leftOver;
const lastChange =
item?.lastChange &&
item?.lastChange?.date &&
`${item?.lastChange?.fullName} (${getFaUserRole(
item?.lastChange?.role
)}) در تاریخ ${formatTime(item?.lastChange?.date)}`;
return [
<OperationsCell
key={`operations-${item?.key || i}`}
item={item}
updateArchive={(pageNum) => fetchApiData(pageNum || page, textValue)}
readOnly={readOnly}
/>,
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.chainCompany?.name && item?.chainCompany?.user?.mobile
? `${item.chainCompany.name} (${item.chainCompany.user.mobile})`
: "-",
<Typography
key={`status-${item?.key || i}`}
variant="body2"
color={item?.violation ? "error" : "black"}
>
{item?.violation ? "متخلف" : "عادی"}
</Typography>,
item?.poultry?.unitName || "-",
item?.poultry?.userprofile?.fullName &&
item?.poultry?.userprofile?.mobile
? `${item.poultry.userprofile.fullName} (${item.poultry.userprofile.mobile})`
: "-",
item?.poultry?.address?.city?.name
? `${item.poultry.address.city.name}/${
item?.poultry?.cityOperator
? item.poultry.cityOperator
: "بدون تعاونی"
}`
: "-",
item?.vetFarm?.vetFarmMobile
? `${item.vetFarm.vetFarmFullName} (${item.vetFarm.vetFarmMobile})`
: "-",
item?.hall || "-",
item?.period || "-",
formatTime(item?.createDate) || "-",
formatTime(item?.date) || "-",
item?.chickenBreed || "-",
item?.age || "-",
item?.quantity ? item.quantity.toLocaleString() : "-",
item?.losses && item?.quantity
? `${item.losses} (%${((item.losses * 100) / item.quantity).toFixed(
0
)})`
: "-",
item?.outProvinceKilledQuantity
? item.outProvinceKilledQuantity.toLocaleString()
: "-",
item?.governmentalKilledQuantity
? item.governmentalKilledQuantity.toLocaleString()
: "-",
item?.freeKilledQuantity
? item.freeKilledQuantity.toLocaleString()
: "-",
item?.outProvinceKilledWeight
? item.outProvinceKilledWeight.toLocaleString()
: "-",
item?.chainKilledQuantity
? item.chainKilledQuantity.toLocaleString()
: "-",
item?.chainKilledWeight ? item.chainKilledWeight.toLocaleString() : "-",
item?.quantity && killedNumber !== undefined
? `${killedNumber.toLocaleString()} (%${(
(killedNumber * 100) /
item.quantity
).toFixed(0)})`
: "-",
item?.leftOver && item?.quantity
? `${item.leftOver.toLocaleString()} (%${(
(item.leftOver * 100) /
item.quantity
).toFixed(0)})`
: "-",
item?.activeKill?.activeKill ? "دارد" : "ندارد",
item?.activeKill?.countOfRequest || "-",
lastChange || "-",
];
});
setTableData(transformedData);
}, [data, page, perPage, textValue, readOnly]);
const handleSubmit = async (event) => {
event.preventDefault();
setPage(1);
fetchApiData(1, textValue);
};
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>آرشیو جوجه ریزی </Typography>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}archive_hatching_excel/?chain=true&key=${userKey}&role=${getRoleFromUrl()}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</form>
</Grid>
</Grid>
);
return (
<Grid alignItems="center" justifyContent="center" xs={12}>
<ResponsiveTable
title={tableTitle}
columns={columns}
data={tableData}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePageChange={handlePageChange}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
);
};

View File

@@ -0,0 +1,112 @@
import { Button, IconButton, TextField, Typography } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { Grid } from "../../../../components/grid/Grid";
import EditIcon from "@mui/icons-material/Edit";
import { provinceChainsEditAllocation } from "../../services/province-chains-edit-allocation";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
export const ChainBarManagementEditHealthCode = ({
healthCode,
item,
updateTable,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleOpenModal = () => {
dispatch(
OPEN_MODAL({
title: "ویرایش کد بهداشتی",
size: "auto",
content: (
<EditHealthCodeModal
initialValue={healthCode || ""}
onSubmit={(newValue) => {
dispatch(
provinceChainsEditAllocation({
chain_allcation_key: item?.key,
health_code: newValue,
})
).then((r) => {
if (r.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
onCancel={() => dispatch(CLOSE_MODAL())}
/>
),
})
);
};
return (
<Grid
container
alignItems="center"
gap={0.5}
sx={{ width: "120px" }}
justifyContent="center"
>
<Typography variant="caption">{healthCode || "-"}</Typography>
<IconButton
aria-label="edit"
size="small"
color="primary"
onClick={handleOpenModal}
>
<EditIcon fontSize="small" />
</IconButton>
</Grid>
);
};
const EditHealthCodeModal = ({ initialValue, onSubmit, onCancel }) => {
const [value, setValue] = useState(initialValue);
return (
<Grid container direction="column" gap={2} minWidth={260}>
<TextField
label="کد بهداشتی"
value={value}
onChange={(event) => setValue(event.target.value)}
fullWidth
size="small"
/>
<Grid container justifyContent="flex-end" xs={12} gap={1}>
<Button
variant="contained"
color="primary"
onClick={() => onSubmit(value)}
style={{ flex: 1 }}
>
ثبت
</Button>
<Button
style={{ flex: 1 }}
variant="outlined"
color="primary"
onClick={onCancel}
>
لغو
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,209 @@
import {
IconButton,
Popover,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
} from "@mui/material";
import React, { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import SettingsIcon from "@mui/icons-material/Settings";
import TuneIcon from "@mui/icons-material/Tune";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ChainsAllocation } from "../chains-allocation/ChainsAllocation";
import { provinceChainsDeleteAllocation } from "../../services/province-chains-delete-allocation";
import { AppContext } from "../../../../contexts/AppContext";
import { ProvinceCheckChainAllocation } from "../province-check-chain-allocation/ProvinceCheckChainAllocation";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
export const ChainBarManagementOperations = ({ item, fetchApiData }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const role = getRoleFromUrl();
const handleApprove = () => {
handleClose();
dispatch(
DRAWER({
title: "تایید/ رد درخواست",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<ProvinceCheckChainAllocation
item={item}
fetchApiData={fetchApiData}
/>
),
})
);
};
const handleEdit = () => {
handleClose();
dispatch(
DRAWER({
title: "ویرایش بار",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<ChainsAllocation item={item} updateTable={fetchApiData} isEdit />
),
})
);
};
const handleDelete = () => {
handleClose();
dispatch(provinceChainsDeleteAllocation(item?.key)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
fetchApiData(1);
}
});
};
const canManage =
role === "ProvinceOperator" || role === "SuperAdmin" || role === "AdminX";
const options = [
{
key: "approve",
label: "تایید / رد درخواست",
color: "primary.main",
icon: <SettingsIcon sx={{ fontSize: { xs: 20, md: 18 } }} />,
action: handleApprove,
disabled: item?.state !== "pending",
},
{
key: "edit",
label: "ویرایش بار",
color: "info.main",
icon: <EditIcon sx={{ fontSize: { xs: 20, md: 18 } }} />,
action: handleEdit,
disabled:
!canManage ||
item?.state === "rejected" ||
Boolean(item?.quarantineCode),
},
{
key: "delete",
label: "حذف بار",
color: "error.main",
icon: <DeleteIcon sx={{ fontSize: { xs: 20, md: 18 } }} />,
action: handleDelete,
disabled:
!canManage ||
item?.state === "rejected" ||
Boolean(item?.quarantineCode),
},
];
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
slotProps={{
paper: {
sx: {
mt: 1,
},
},
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<List sx={{ p: 1 }}>
{options.map((option) => (
<ListItemButton
key={option.key}
onClick={() => {
if (option.disabled) {
return;
}
option.action();
}}
disabled={Boolean(option.disabled)}
sx={{
borderRadius: 1,
mb: 0.5,
color: option.disabled ? "text.disabled" : option.color,
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon
sx={{
color: option.disabled ? "text.disabled" : option.color,
minWidth: 36,
}}
>
{option.icon}
</ListItemIcon>
<ListItemText
primary={
<Typography
sx={{
color: option.disabled ? "text.disabled" : option.color,
fontWeight: 600,
fontSize: { xs: "13px", md: "14px" },
}}
>
{option.label}
</Typography>
}
/>
</ListItemButton>
))}
</List>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,506 @@
import React, { useContext, useEffect, useState } from "react";
import { Button, Tab, Tabs, TextField, Tooltip } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import {
ROUTE_ADMINX_CHAINS,
ROUTE_PARENT_COMPANY_ALLOCATIONS,
ROUTE_PROVINCE_CHAINS,
ROUTE_SUPER_ADMIN_CHAINS,
} from "../../../../routes/routes";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useLocation } from "react-router-dom";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { ChainBarManagementEditHealthCode } from "../chain-bar-management-edit-health-code/ChainBarManagementEditHealthCode";
import { ChainBarManagementOperations } from "../chain-bar-management-operations/ChainBarManagementOperations";
import { format } from "date-fns-jalali";
import { ChainSubmitQuarantineCode } from "../chain-submit-quarantine-code/ChainSubmitQuarantineCode";
import { formatJustDate } from "../../../../utils/formatTime";
export const ChainBarManagement = ({ province }) => {
const [
openNotif,
,
selectedDate1,
setSelectedDate1,
selectedDate2,
setSelectedDate2,
] = useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const { pathname } = useLocation();
const userKey = useSelector((state) => state.userSlice?.userProfile?.key);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [tableDataRejected, setTableDataRejected] = useState([]);
const [has500Error, setHas500Error] = useState(false);
const fetchApiData = async (page) => {
// Prevent request if there was a 500 error
if (has500Error) {
return;
}
dispatch(LOADING_START());
try {
const response = await axios.get(
`${
province ? province + "parent-company-" : ""
}chain-allocation/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}&date1=${selectedDate1}&date2=${selectedDate2}${
value === 1 ? "&state=rejected" : ""
}`
);
// Reset error state on successful response
setHas500Error(false);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
} catch (error) {
console.error("Error fetching data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setData([]);
setTotalRows(0);
} finally {
dispatch(LOADING_END());
}
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
setTableDataRejected([]);
return;
}
const d = data.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
<ChainBarManagementOperations
key={i}
fetchApiData={updateTable}
item={item}
/>,
item?.state === "accepted"
? "تایید شده"
: item?.state === "pending"
? "در انتظار تایید"
: "رد شده",
item?.date ? formatJustDate(item.date) : "-",
item?.outProvince ? "خارج استان" : "داخل استان",
`${item?.registerer?.fullname || ""} (${
item?.registerer?.mobile || ""
})`,
item?.killHouse?.killHouseOperator?.user?.fullname
? `${item.killHouse.killHouseOperator.user.fullname} (${
item?.killHouse?.killHouseOperator?.user?.mobile || ""
})`
: `${item?.buyerName || ""} (${item?.buyerMobile || ""})`,
`${item?.poultryHatching?.poultry?.unitName || ""} (${
item?.poultryHatching?.poultry?.user?.mobile || ""
})`,
`${item?.chainCompany?.name || ""} (${
item?.chainCompany?.user?.mobile || ""
})`,
<>
{" "}
{getRoleFromUrl() === "ParentCompany" ? (
item?.healthCode || ""
) : (
<ChainBarManagementEditHealthCode
healthCode={item?.healthCode}
item={item}
updateTable={updateTable}
/>
)}
</>,
<>
{getRoleFromUrl() === "VetFarm" ||
getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "SuperAdmin" ||
getRoleFromUrl() === "Supporter" ||
getRoleFromUrl() === "AdminX" ||
getRoleFromUrl() === "CityVet" ||
getRoleFromUrl() === "VetSupervisor" ? (
<>
<ChainSubmitQuarantineCode
isLocked={
getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "VetSupervisor"
? false
: item?.trash === true ||
// item.assignmentStateArchive === "True" ||
// item.vetState === "accepted" ||
item?.quarantineQuantity
? true
: false
}
item={item}
updateTable={updateTable}
/>
{item?.quarantineCodeRegisterar?.date
? (() => {
try {
const date = new Date(item?.registerar?.date);
return isNaN(date.getTime())
? ""
: `${format(date, "yyyy/MM/dd")} ${
item?.quarantineCodeRegisterar?.name || ""
}`;
} catch (error) {
console.error(
"Error formatting quarantineCodeRegisterar date:",
error
);
return "";
}
})()
: ""}
</>
) : item?.quarantineCode ? (
item.quarantineCode
) : (
"-"
)}
</>,
item?.driverName
? `${item.driverName} (${item?.driverMobile || ""})`
: "-",
item?.typeCar || "",
item?.pelak || "",
item?.quantity || 0,
item?.indexWeight || 0,
item?.weight || 0,
];
});
setTableData(d);
const r = data.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.state === "rejected"
? "رد شده"
: item?.state === "deleted"
? "حذف شده"
: "در انتظار تایید",
item?.remover
? `${item.remover?.fullname || ""} (${item.remover?.mobile || ""})`
: `${item?.registerer?.fullname || ""} (${
item?.registerer?.mobile || ""
})`,
item?.date ? formatJustDate(item.date) : "-",
item?.outProvince ? "خارج استان" : "داخل استان",
item?.registerer?.fullname
? `${item.registerer.fullname} (${item?.registerer?.mobile || ""})`
: "-",
item?.killHouse?.killHouseOperator?.user?.fullname
? `${item.killHouse.killHouseOperator.user.fullname} (${
item?.killHouse?.killHouseOperator?.user?.mobile || ""
})`
: `${item?.buyerName || ""} (${item?.buyerMobile || ""})`,
`${item?.poultryHatching?.poultry?.unitName || ""} (${
item?.poultryHatching?.poultry?.user?.mobile || ""
})`,
`${item?.chainCompany?.name || ""} (${
item?.chainCompany?.user?.mobile || ""
})`,
item?.healthCode || "",
item?.quarantineCode || "",
item?.driverName
? `${item.driverName} (${item?.driverMobile || ""})`
: "-",
item?.typeCar || "",
item?.pelak || "",
item?.quantity || 0,
item?.indexWeight || 0,
item?.weight || 0,
];
});
setTableDataRejected(r);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, selectedDate1, selectedDate2, perPage, value]);
// Reset error state when dates, filters, or tab value changes
useEffect(() => {
setHas500Error(false);
}, [selectedDate1, selectedDate2, textValue, value]);
const handleSubmit = async (event) => {
event.preventDefault();
// Reset error state on manual submit (user retry)
setHas500Error(false);
dispatch(LOADING_START());
try {
const response = await axios.get(
`${
province ? province + "parent-company-" : ""
}chain-allocation/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}&date1=${selectedDate1}&date2=${selectedDate2}${
value === 1 ? "&state=rejected" : ""
}`
);
// Reset error state on successful response
setHas500Error(false);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setData([]);
setTotalRows(0);
dispatch(LOADING_END());
}
};
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
xs={12}
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid style={{ width: "150px" }}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid style={{ width: "150px" }}>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
{value === 0 ? (
<Tooltip title="خروجی اکسل">
<a
href={`${
province ? province : axios.defaults.baseURL
}bar_chain_excel/?key=${
userKey || ""
}&role=${getRoleFromUrl()}&search=filter&value=${
textValue || ""
}&state=accepted&date1=${selectedDate1 || ""}&date2=${
selectedDate2 || ""
}&search=filter&value=${textValue || ""}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
) : (
<Tooltip title="خروجی اکسل">
<a
href={`${
province ? province : axios.defaults.baseURL
}bar_chain_excel/?key=${
userKey || ""
}&role=${getRoleFromUrl()}&search=filter&value=${
textValue || ""
}&state=rejected&date1=${selectedDate1 || ""}&date2=${
selectedDate2 || ""
}&search=filter&value=${textValue || ""}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
)}
</form>
</Grid>
</Grid>
);
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid xs={12} container justifyContent="center" alignItems="center">
{(pathname === ROUTE_PROVINCE_CHAINS ||
pathname === ROUTE_SUPER_ADMIN_CHAINS ||
pathname === ROUTE_ADMINX_CHAINS ||
pathname === ROUTE_PARENT_COMPANY_ALLOCATIONS) && (
<Tabs
value={value}
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab label="درخواست های تایید شده" />
<Tab label="درخواست های رد شده" />
</Tabs>
)}
</Grid>
{tableTitle}
<ResponsiveTable
data={value === 0 ? tableData : tableDataRejected}
columns={
value === 0
? [
"ردیف",
"عملیات",
"وضعیت",
"تاریخ سفارش",
"نوع فروش",
"ثبت کننده سفارش",
"خریدار",
"مرغداری",
"شرکت زنجیره",
"کد بهداشتی",
"کد قرنطینه",
"راننده",
"نوع خودرو",
"پلاک",
"حجم",
"میانگین وزن",
"وزن",
]
: [
"ردیف",
"وضعیت",
"حذف/رد کننده",
"تاریخ سفارش",
"نوع فروش",
"خریدار",
"مرغداری",
"شرکت زنجیره",
"کد بهداشتی",
"کد قرنطینه",
"راننده",
"نوع خودرو",
"پلاک",
"حجم",
"میانگین وزن",
"وزن",
]
}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={value === 0 ? "بارهای تایید شده" : "بارهای رد شده"}
/>
</Grid>
);
};

View File

@@ -0,0 +1,108 @@
import React, { useContext, useEffect } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { Button, TextField } from "@mui/material";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { provinceChainsEditBar } from "../../services/province-chains-edit-bar";
export const ChainEditBar = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
weight: item?.weight ? item?.weight : "",
quantity: item?.quantity ? item?.quantity : "",
},
validationSchema: Yup.object({
weight: Yup.number().required("این فیلد اجباری است!"),
quantity: Yup.number().required("این فیلد اجباری است!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container xs={12} alignItems="center" justifyContent="center" gap={2}>
<Grid xs={12}>
<TextField
id="quantity"
fullWidth
label="مقدار"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.quantity}
error={
formik.touched.quantity ? Boolean(formik.errors.quantity) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.quantity && Boolean(formik.errors.quantity)
? formik.errors.quantity
: null
}
/>
</Grid>
<Grid xs={12}>
<TextField
id="weight"
fullWidth
label="وزن"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.weight}
error={formik.touched.weight ? Boolean(formik.errors.weight) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
/>
</Grid>
<Grid xs={12}>
<Button
disabled={!formik.isValid}
variant="contained"
fullWidth
onClick={() => {
dispatch(
provinceChainsEditBar({
chain_allcation_key: item?.key,
quantity: formik.values.quantity,
weight: formik.values.weight,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد!",
severity: "success",
});
}
});
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,89 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useDispatch } from "react-redux";
import { Autocomplete, Button, TextField } from "@mui/material";
import { AppContext } from "../../../../contexts/AppContext";
import { provinceChainsReplaceActiveChain } from "../../services/province-chains-replace-active-chain";
import { provinceChainsGetCompanies } from "../../services/province-chains-get-companies";
export const ChainsReplaceActiveChain = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [dataCompany, setDataCompany] = useState([]);
const [selectedCompany, setSelectedCompany] = useState("");
const [openNotif] = useContext(AppContext);
useEffect(() => {
dispatch(provinceChainsGetCompanies()).then((r) => {
setDataCompany(r.payload.data);
});
}, []);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<Autocomplete
disablePortal
id="province"
options={
dataCompany
? dataCompany?.map((i) => ({
id: i.key,
label: `${i.name} (${i.user.mobile})`,
}))
: []
}
onChange={(e, value) => {
setSelectedCompany(value.id);
}}
renderInput={(params) => (
<TextField {...params} label="شرکت زنجیره" />
)}
/>
</Grid>
</Grid>
<Grid>
<Button
disabled={!selectedCompany}
variant="contained"
fullWidth
onClick={() => {
dispatch(
provinceChainsReplaceActiveChain({
type: "replace",
hatching_key: item?.key,
company_key: selectedCompany,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,220 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import {
Button,
Grid,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch } from "react-redux";
import PageviewIcon from "@mui/icons-material/Pageview";
import EditIcon from "@mui/icons-material/Edit";
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
import { PropTypes } from "prop-types";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceChainsEditBar } from "../../services/province-chains-edit-bar";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
export const ChainSubmitQuarantineCode = ({ item, updateTable, isLocked }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleOpenModal = () => {
dispatch(
OPEN_MODAL({
title: "ثبت کد قرنطینه",
size: "auto",
content: (
<SubmitQuarantineModal
code={item.quarantineCode || ""}
isLocked={isLocked}
onSubmit={(newCode) => {
dispatch(
provinceChainsEditBar({
chain_allcation_key: item.key,
quarantine_code: newCode,
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
onCancel={() => dispatch(CLOSE_MODAL())}
/>
),
})
);
};
return (
<Grid
container
alignItems="center"
gap={0.5}
sx={{
width: item.quarantineCode ? "160px" : "auto",
justifyContent: "center",
}}
>
{item.quarantineCode ? (
<ViewCodeComponent clearanceCode={item.quarantineCode} />
) : (
<IconButton
aria-label="add"
size="small"
color="primary"
onClick={handleOpenModal}
>
<AddCircleOutlineIcon fontSize="small" />
</IconButton>
)}
{!isLocked && (
<IconButton
aria-label="edit"
size="small"
color="primary"
onClick={handleOpenModal}
sx={{ display: item.quarantineCode ? "inline-flex" : "none" }}
>
<EditIcon fontSize="small" />
</IconButton>
)}
</Grid>
);
};
const ViewCodeComponent = ({ clearanceCode }) => {
const formRef = useRef(null);
const handleImageClick = () => {
if (formRef.current) {
formRef.current.submit();
}
};
return (
<Grid
container
alignItems="center"
justifyContent="center"
gap={0.5}
style={{ width: "auto" }}
>
<Tooltip title="مشاهده گواهینامه" arrow>
<form
action="https://e.ivo.ir/Rahgiri/Gidprnt.aspx"
method="post"
target="_blank"
ref={formRef}
style={{
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<input name="gid" type="hidden" value={clearanceCode} />
<PageviewIcon color="primary" onClick={handleImageClick} />
</form>
</Tooltip>
<Typography variant="caption" color="primary">
{clearanceCode}
</Typography>
</Grid>
);
};
const SubmitQuarantineModal = ({ code, isLocked, onSubmit, onCancel }) => {
const [value, setValue] = useState(code);
const [error, setError] = useState("");
useEffect(() => {
setValue(code);
}, [code]);
const validate = (val) => {
if (!val) {
setError("این فیلد اجباری است");
return false;
}
const pattern = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+$/;
if (!pattern.test(val)) {
setError("باید شامل اعداد و حروف انگلیسی باشد");
return false;
}
setError("");
return true;
};
const handleChange = (event) => {
const newValue = event.target.value.toUpperCase();
setValue(newValue);
validate(newValue);
};
const handleSubmit = () => {
if (validate(value)) {
onSubmit(value);
}
};
return (
<Grid container direction="column" gap={2}>
<TextField
label="کد قرنطینه"
value={value}
onChange={handleChange}
size="small"
fullWidth
error={Boolean(error)}
helperText={error}
InputProps={{
readOnly: isLocked,
}}
/>
<Grid container justifyContent="flex-end" gap={1} xs={12}>
<Button
variant="contained"
color="primary"
onClick={handleSubmit}
disabled={isLocked}
style={{ flex: 1 }}
>
ثبت
</Button>
<Button
style={{ flex: 1 }}
variant="outlined"
color="primary"
onClick={onCancel}
>
لغو
</Button>
</Grid>
</Grid>
);
};
ChainSubmitQuarantineCode.propTypes = {
item: PropTypes.any,
updateTable: PropTypes.any,
isLocked: PropTypes.any,
};

View File

@@ -0,0 +1,257 @@
import {
IconButton,
Popover,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
} from "@mui/material";
import { useContext, useState } from "react";
import ArchiveIcon from "@mui/icons-material/Archive";
import DeleteIcon from "@mui/icons-material/Delete";
import TuneIcon from "@mui/icons-material/Tune";
import FindReplaceIcon from "@mui/icons-material/FindReplace";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { provinceChainsDeleteChain } from "../../services/province-chains-delete-chain";
import { ChainsReplaceActiveChain } from "../chain-replace-active-chain/ChainReplaceActiveChain";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { CityArchiveHatchingDrawer } from "../../../city/components/city-archive-hatching-drawer/CityArchiveHatchingDrawer";
import AddTaskIcon from "@mui/icons-material/AddTask";
import { ChainsAllocation } from "../chains-allocation/ChainsAllocation";
export const ChainsActiveChainsOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
slotProps={{
paper: {
sx: {
mt: 1,
},
},
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<List sx={{ p: 1 }}>
<ListItemButton
onClick={() => {
handleClose();
dispatch(
DRAWER({
title: "تخصیص به زنجیره",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<ChainsAllocation item={item} updateTable={updateTable} />
),
})
);
}}
sx={{
borderRadius: 1,
mb: 0.5,
color: "primary.main",
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: "primary.main", minWidth: 36 }}>
<AddTaskIcon sx={{ fontSize: { xs: 20, md: 18 } }} />
</ListItemIcon>
<ListItemText
primary={
<Typography
sx={{
color: "primary.main",
fontWeight: 600,
fontSize: { xs: "13px", md: "14px" },
}}
>
تخصیص به زنجیره
</Typography>
}
/>
</ListItemButton>
<ListItemButton
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "جایگزین کردن مرغداری ",
content: (
<ChainsReplaceActiveChain
item={item}
updateTable={updateTable}
/>
),
})
);
}}
sx={{
borderRadius: 1,
mb: 0.5,
color: "info.main",
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: "info.main", minWidth: 36 }}>
<FindReplaceIcon sx={{ fontSize: { xs: 20, md: 18 } }} />
</ListItemIcon>
<ListItemText
primary={
<Typography
sx={{
color: "info.main",
fontWeight: 600,
fontSize: { xs: "13px", md: "14px" },
}}
>
جایگزین کردن
</Typography>
}
/>
</ListItemButton>
<ListItemButton
onClick={() => {
handleClose();
dispatch(
DRAWER({
title: "انتقال به آرشیو",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<CityArchiveHatchingDrawer
item={item}
updateTable={updateTable}
/>
),
})
);
}}
sx={{
borderRadius: 1,
mb: 0.5,
color: "secondary.main",
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: "secondary.main", minWidth: 36 }}>
<ArchiveIcon sx={{ fontSize: { xs: 20, md: 18 } }} />
</ListItemIcon>
<ListItemText
primary={
<Typography
sx={{
color: "secondary.main",
fontWeight: 600,
fontSize: { xs: "13px", md: "14px" },
}}
>
انتقال به بایگانی
</Typography>
}
/>
</ListItemButton>
<ListItemButton
onClick={() => {
handleClose();
dispatch(
provinceChainsDeleteChain({
type: "delete",
hatching_key: item?.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
sx={{
borderRadius: 1,
mb: 0.5,
color: "error.main",
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: "error.main", minWidth: 36 }}>
<DeleteIcon sx={{ fontSize: { xs: 20, md: 18 } }} />
</ListItemIcon>
<ListItemText
primary={
<Typography
sx={{
color: "error.main",
fontWeight: 600,
fontSize: { xs: "13px", md: "14px" },
}}
>
خروج از زنجیره
</Typography>
}
/>
</ListItemButton>
</List>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,437 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { ChainsActiveChainsOperations } from "../chains-active-chains-operations/ChainsActiveChainsOperations";
import { ChainsSubmitActiveChain } from "../chains-submit-active-chain/ChainsSubmitActiveChain";
import { convertToIranianTime } from "../../../../utils/formatTime";
export const ChainsActiveChains = () => {
const dispatch = useDispatch();
const [selectedAge1, setSelectedAge1] = useState(0);
const [selectedAge2, setSelectedAge2] = useState(0);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [openNotif] = useContext(AppContext);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
let response;
dispatch(LOADING_START());
response = await axios.get(
`poultry_hatching?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&chain=true`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
const killedNumber = (item) => {
let killedNumber = "";
killedNumber = item.quantity - item.losses - item.leftOver;
return killedNumber;
};
useEffect(() => {
const d = data?.map((item, i) => {
let lastChange;
return [
<ChainsActiveChainsOperations
key={i}
item={item}
updateTable={updateTable}
/>,
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
`${item?.chainCompany?.name} (${item?.chainCompany?.user.mobile})`,
item.violation ? "متخلف" : "عادی",
item.poultry.unitName,
`${item.poultry.userprofile.fullName} (${item.poultry.userprofile.mobile})`,
`${item?.poultry?.address.city.name}/${
item?.poultry?.cityOperator
? item?.poultry?.cityOperator
: "بدون تعاونی"
}`,
item?.vetFarm?.vetFarmMobile
? `${item?.vetFarm?.vetFarmFullName} (${item?.vetFarm?.vetFarmMobile})`
: "-",
item.hall,
item.period,
convertToIranianTime(item?.createDate),
convertToIranianTime(item?.date),
item.chickenBreed,
item.age,
item?.quantity?.toLocaleString(),
`${item.losses} (%${((item.losses * 100) / item.quantity).toFixed(0)})`,
`${item?.totalCommitmentQuantity?.toLocaleString()}`,
`${item?.governmentalQuantity?.toLocaleString()}`,
`${item?.governmentalKilledQuantity?.toLocaleString()}`,
`${item?.freeQuantity?.toLocaleString()}`,
`${item?.freeKilledQuantity?.toLocaleString()}`,
`${item?.outProvinceKilledQuantity?.toLocaleString()}`,
`${item?.outProvinceKilledWeight?.toLocaleString()}`,
item?.chainKilledQuantity?.toLocaleString(),
item?.chainKilledWeight?.toLocaleString(),
killedNumber(item)?.toLocaleString() +
` (%${((killedNumber(item) * 100) / item.quantity).toFixed(0)})`,
`${item?.leftOver?.toLocaleString()} (%${(
(item.leftOver * 100) /
item.quantity
).toFixed(0)})`,
item?.totalCommitment?.toLocaleString() + " کیلوگرم ",
item?.governmentalKilledQuantity?.toLocaleString() + " کیلوگرم ",
item?.freeKilledQuantity?.toLocaleString() + " کیلوگرم ",
item?.totalAverageKilledWeight?.toLocaleString() + " کیلوگرم ",
item?.totalKilledWeight?.toLocaleString() + " کیلوگرم ",
item?.activeKill?.activeKill ? "دارد" : "ندارد",
item?.activeKill?.countOfRequest ? item.activeKill.countOfRequest : "-",
(lastChange = item.lastChange
? `${item.lastChange.fullName} (${getFaUserRole(
item.lastChange.role
)}) در تاریخ ${item.lastChange.date}`
: "-"),
(lastChange =
item.latestHatchingChange &&
item.latestHatchingChange.date &&
`${item.latestHatchingChange.fullName} (${getFaUserRole(
item.latestHatchingChange.role
)}) در تاریخ ${item.latestHatchingChange.date}`),
item.latestHatchingChange ? lastChange : "-",
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&chain=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const handleRemoveFilter = async (event) => {
event.preventDefault();
setSelectedAge1(null);
setSelectedAge2(null);
try {
const response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&chain=true&search=filter&value=`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
}
};
const handleSubmitSearchByAge = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`poultry_hatching?role=${getRoleFromUrl()}&age1=${selectedAge1}&age2=${selectedAge2}&chain=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const selectAges = Array.from({ length: 75 }, (_, i) => i + 1);
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
style={{
padding: "10px",
border: "1px solid #bbb",
borderRadius: "10px",
}}
>
<Typography>زنجیره های فعال</Typography>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<Tooltip title="خروجی اکسل">
{/* <a
href={`${axios.defaults.baseURL}0/hatching_excel/`}
rel="noreferrer"
> */}
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}0/hatching_excel/?role=${getRoleFromUrl()}&key=${userKey}&chain=true`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</form>
</Grid>
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
style={{
padding: "10px",
border: "1px solid #bbb",
borderRadius: "10px",
}}
>
<Grid>
<Typography variant="caption">جستجو براساس سن:</Typography>
</Grid>
<Grid style={{ width: "80px" }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">از سن</InputLabel>
<Select
MenuProps={{
PaperProps: {
style: {
maxHeight: 200, // Change the maximum height as needed
width: 80, // Change the width as needed
},
},
}}
labelId="demo-simple-select-label"
id="demo-simple-select"
value={selectedAge1}
label="از سن"
size="small"
onChange={(event) => setSelectedAge1(event.target.value)}
>
{selectAges.map((age) => (
<MenuItem key={age} value={age}>
{age}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid style={{ width: "80px" }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">تا سن</InputLabel>
<Select
MenuProps={{
PaperProps: {
style: {
maxHeight: 200, // Change the maximum height as needed
width: 80, // Change the width as needed
},
},
}}
labelId="demo-simple-select-label"
id="demo-simple-select"
value={selectedAge2}
label="تا سن"
size="small"
onChange={(event) => setSelectedAge2(event.target.value)}
>
{selectAges.map((age) => (
<MenuItem key={age} value={age}>
{age}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Button onClick={handleSubmitSearchByAge} endIcon={<RiSearchLine />}>
جستجو
</Button>
<Tooltip title="خروجی اکسل">
{/* <a
href={`${axios.defaults.baseURL}hatching_report_from_age_excel/?age1=${selectedAge1}&age2=${selectedAge2}`}
rel="noreferrer"
> */}
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${axios.defaults.baseURL}hatching_report_from_age_excel/?age1=${selectedAge1}&age2=${selectedAge2}&chain=true`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<Button onClick={handleRemoveFilter} color="error">
حذف فیلتر
</Button>
</Grid>
);
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid container xs={12}>
<Button
onClick={() => {
dispatch(
DRAWER({
title: "ثبت زنجیره فعال",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <ChainsSubmitActiveChain updateTable={updateTable} />,
})
);
}}
variant="contained"
>
ثبت زنجیره فعال
</Button>
</Grid>
{tableTitle}
<ResponsiveTable
data={tableData}
columns={[
"عملیات",
"ردیف",
"شرکت زنجیره",
"وضعیت",
"نام فارم",
"مرغدار",
"شهر/تعاونی",
"دامپزشک فارم",
"سالن",
"دوره جوجه ریزی",
"تاریخ ثبت جوجه ریزی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"تلفات دوره (قطعه)",
"تعداد کل تعهد دولتی (قطعه)",
"تعداد کشتار شده دولتی (قطعه)",
"وزن کشتار شده دولتی",
"تعداد کشتار شده آزاد (قطعه)",
"وزن کشتار شده آزاد ",
"حجم خارج از استان",
"وزن خارج از استان",
"حخم کشتار زنجیره (قطعه)",
"وزن کشتار زنجیره (کیلوگرم)",
"کشتار شده (قطعه)",
"مانده در سالن (قطعه)",
"وزن تعهد دولتی",
"وزن کشتار دولتی",
"وزن کشتار آزاد",
"میانگین وزن کشتار",
"وزن کل کشتار شده",
"کشتار فعال",
"تعداد درخواست کشتار",
"آخرین تغییر",
"سازنده جوجه ریزی",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="زنجیره"
/>
</Grid>
);
};

View File

@@ -0,0 +1,720 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import moment from "moment";
import {
Button,
Divider,
InputLabel,
MenuItem,
Select,
TextField,
Typography,
} from "@mui/material";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import { provinceChainsSubmitAllocation } from "../../services/province-chains-submut-allocation";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
import { CarPelak } from "../../../../components/car-pelak/CarPelak";
import { SPACING } from "../../../../data/spacing";
import { provinceGetKillHousesForAllocation } from "../../services/province-get-killhouses-for-allocation";
import { provinceChainsEditAllocation } from "../../services/province-chains-edit-allocation";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const ChainsAllocation = ({ item, updateTable, isEdit }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [value, setValue] = React.useState(
isEdit ? (item?.outProvince ? "out" : "native") : "out"
);
const [selectedKillhouse, setSelectedKillHouse] = React.useState();
useEffect(() => {
if (value === "native") {
dispatch(LOADING_START());
dispatch(provinceGetKillHousesForAllocation()).then((r) => {
dispatch(LOADING_END());
const d = r?.payload.data.map((item) => {
return { name: item.name, key: item.key };
});
setKillhouses(d);
setSelectedKillHouse("");
});
}
}, [value]);
const [killhouses, setKillhouses] = useState([]);
const handleChange = (event) => {
setValue(event.target.value);
};
const [driverPelak, setDriverPelak] = useState([]);
const carPelakHandleChange = (pelak1, pelak2, pelak3, pelak4) => {
setDriverPelak([pelak1, pelak2, pelak3, pelak4]);
};
const formik = useFormik({
initialValues: {
weight: isEdit ? item?.indexWeight : "",
quantity: isEdit ? item?.quantity : "",
driverName: isEdit ? item?.driverName : "",
driverMobile: isEdit ? item?.driverMobile : "",
driverCar: isEdit ? item?.typeCar : "",
healthCode: isEdit ? item?.healthCode : "",
},
validationSchema: Yup.object({
weight: Yup.number().required("این فیلد اجباری است!"),
quantity: Yup.number().required("این فیلد اجباری است!"),
driverName: Yup.string().required("این فیلد اجباری است!"),
healthCode: Yup.string().required("این فیلد اجباری است!"),
driverMobile: Yup.string()
.required("شماره موبایل کشتارگاه الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
driverCar: Yup.string().required("این فیلد اجباری است!"),
// driverhealthCode: Yup.string().required("این فیلد اجباری است!"),
}),
});
const [, , selectedDate1, setSelectedDate1, ,] = useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
}, []);
const formikbuyer = useFormik({
initialValues: {
name: isEdit ? item?.buyerName : "",
mobile: isEdit ? item?.buyerMobile : "",
province: isEdit ? item?.province : "",
city: isEdit ? item?.city : "",
},
validationSchema: Yup.object({
name: Yup.string().required("نام اجباری است"),
mobile: Yup.string()
.required("شماره موبایل کشتارگاه الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
province: Yup.string().required("استان اجباری است"),
city: Yup.string().required("شهرستان اجباری است"),
}),
});
useEffect(() => {
formik.validateForm();
formikbuyer.validateForm();
}, []);
const isFormValid = () => {
if (value === "native") {
return formik.isValid && selectedKillhouse && driverPelak[1];
} else {
return formik.isValid && formikbuyer.isValid && driverPelak[1];
}
};
useEffect(() => {
let newVal = formik.values.weight;
const mystring = formik.values.weight.toString().split(".").join("");
if (formik.values.weight) {
if (mystring.length <= 3) {
if (mystring.length === 2) {
newVal = mystring[0] + "." + mystring[1];
}
if (mystring.length === 3) {
newVal = mystring[0] + "." + mystring[1] + mystring[2];
}
}
}
if (isNaN(Number.parseFloat(newVal))) {
formik.setFieldValue("weight", "");
} else {
formik.setFieldValue("weight", Number.parseFloat(newVal));
}
}, [formik.values.weight]);
return (
<Grid container xs={12} alignItems="center" justifyContent="center" gap={2}>
{!isEdit && (
<>
<Grid container xs={12}>
<Typography variant="body2">شرکت زنجیره:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item?.companyName}
</Typography>
</Grid>
<Grid container xs={12}>
<Typography variant="body2">مرغدار:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item.poultry.userprofile.fullName} (
{item.poultry.userprofile.mobile})
</Typography>
</Grid>
<Grid container xs={12}>
<Typography variant="body2">نژاد:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item.chickenBreed}
</Typography>
</Grid>
<Grid container xs={12}>
<Typography variant="body2">سن جوجه:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item.age}
</Typography>
</Grid>
</>
)}
{isEdit && (
<>
<Grid container xs={12}>
<Typography variant="body2">مرغدار:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item?.poultryHatching?.poultry?.unitName} (
{item?.poultryHatching?.poultry?.user?.mobile})
</Typography>
</Grid>
<Grid container xs={12}>
<Typography variant="body2">شرکت زنجیره:{" "}</Typography>
<Typography variant="body2" style={{ color: "gray" }}>
{item?.chainCompany?.name}
</Typography>
</Grid>
</>
)}
<Divider style={{ width: "100%" }} />
<Grid xs={12}>
<DatePicker
label=" تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid xs={12}>
<TextField
id="quantity"
fullWidth
label="حجم (قطعه)"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.quantity}
error={
formik.touched.quantity ? Boolean(formik.errors.quantity) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.quantity && Boolean(formik.errors.quantity)
? formik.errors.quantity
: null
}
/>
</Grid>
<Grid xs={12}>
<TextField
id="weight"
fullWidth
label="میانگین وزن"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.weight}
error={formik.touched.weight ? Boolean(formik.errors.weight) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
/>
</Grid>
{formik.values.weight && formik.values.quantity && (
<Grid
container
style={{
borderStyle: "solid",
borderWidth: "2px",
borderColor: "gray",
borderRadius: "6px",
}}
xs={12}
p={1}
>
<Typography variant="body2" color="primary">
وزن کل: {" "}
</Typography>
<Typography variant="body2">
{parseInt(formik.values.quantity * formik.values.weight)} کیلوگرم
</Typography>
</Grid>
)}
<FormControl>
<FormLabel id="demo-controlled-radio-buttons-group">نوع فروش</FormLabel>
<RadioGroup
row
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}
>
<FormControlLabel
value="out"
control={<Radio disabled={isEdit} />}
label="خارج استان"
/>
<FormControlLabel
value="native"
control={<Radio disabled={isEdit} />}
label="درون استان"
/>
</RadioGroup>
</FormControl>
{value === "out" ? (
<Grid
container
xs={12}
alignItems="center"
justifyContent="center"
gap={SPACING.SMALL}
>
<Typography variant="body1">اطلاعات خریدار</Typography>
<TextField
label="نام و نام خانوادگی"
name="name"
value={formikbuyer.values.name}
onChange={formikbuyer.handleChange}
onBlur={formik.handleBlur}
error={formikbuyer.touched.name && Boolean(formikbuyer.errors.name)}
helperText={formikbuyer.touched.name && formikbuyer.errors.name}
required
fullWidth
/>
<TextField
fullWidth
id="mobile"
value={formikbuyer.values.mobile}
error={
formikbuyer.touched.mobile
? Boolean(formikbuyer.errors.mobile)
: null
}
onChange={formikbuyer.handleChange}
onBlur={formikbuyer.handleBlur}
helperText={
formikbuyer.touched.mobile && Boolean(formikbuyer.errors.mobile)
? formikbuyer.errors.mobile
: null
}
label="موبایل"
autoComplete="current-password"
variant="outlined"
/>
<TextField
label="استان"
name="province"
value={formikbuyer.values.province}
onChange={formikbuyer.handleChange}
onBlur={formik.handleBlur}
error={
formikbuyer.touched.province &&
Boolean(formikbuyer.errors.province)
}
helperText={
formikbuyer.touched.province && formikbuyer.errors.province
}
required
fullWidth
/>
<TextField
label="شهرستان"
name="city"
value={formikbuyer.values.city}
onBlur={formik.handleBlur}
onChange={formikbuyer.handleChange}
error={formikbuyer.touched.city && Boolean(formikbuyer.errors.city)}
helperText={formikbuyer.touched.city && formikbuyer.errors.city}
required
fullWidth
/>
</Grid>
) : (
<Grid xs={12}>
<FormControl
fullWidth
error={
formik.errors.selectedOption && formik.touched.selectedOption
}
>
<InputLabel>کشتارگاه را انتخاب کنید</InputLabel>
<Select
fullWidth
label="کشتارگاه را انتخاب کنید"
id="selectedKillhouse"
name="selectedKillhouse"
onChange={(e) => {
const [key] = e.target.value.split("#");
setSelectedKillHouse(key);
}}
onBlur={formik.handleBlur}
>
{killhouses?.map((option) => (
<MenuItem
key={option}
value={`${option.key + "#" + option.killer}`}
>
{`کشتارگاه ${option.name}`}
</MenuItem>
))}
</Select>
{formik.errors.selectedKillhouse &&
formik.touched.selectedKillhouse && (
<div>{formik.errors.selectedKillhouse}</div>
)}
</FormControl>
</Grid>
)}
<Grid
container
xs={12}
alignItems="center"
justifyContent="center"
gap={SPACING.SMALL}
>
<Typography variant="body1">اطلاعات خودرو حمل</Typography>
<TextField
label="نام و نام خانوادگی راننده"
name="driverName"
value={formik.values.driverName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.driverName && Boolean(formik.errors.driverName)}
helperText={
formik.touched.driverName && Boolean(formik.errors.driverName)
? formik.errors.driverName
: null
}
required
fullWidth
/>
<TextField
fullWidth
id="driverMobile"
value={formik.values.driverMobile}
error={
formik.touched.driverMobile
? Boolean(formik.errors.driverMobile)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.driverMobile && Boolean(formik.errors.driverMobile)
? formik.errors.driverMobile
: null
}
label="موبایل راننده"
autoComplete="current-password"
variant="outlined"
/>
<TextField
label="کد بهداشتی "
name="healthCode"
value={formik.values.healthCode}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.healthCode && Boolean(formik.errors.healthCode)}
helperText={formik.touched.healthCode && formik.errors.healthCode}
type="tel"
required
fullWidth
/>
<TextField
label="مدل خودرو"
name="driverCar"
value={formik.values.driverCar}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.driverCar && Boolean(formik.errors.driverCar)}
helperText={formik.touched.driverCar && formik.errors.driverCar}
type="tel"
required
fullWidth
/>
<CarPelak
width="100%"
handleChange={carPelakHandleChange}
pelakInitial={isEdit ? item?.pelak : ""}
/>
{/* <TextField
label="کد بهداشتی حمل"
name="driverhealthCode"
value={formik.values.driverhealthCode}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.driverhealthCode &&
Boolean(formik.errors.driverhealthCode)
}
helperText={
formik.touched.driverhealthCode && formik.errors.driverhealthCode
}
required
fullWidth
/> */}
</Grid>
<Button
disabled={!isFormValid()}
variant="contained"
fullWidth
onClick={() => {
if (
getRoleFromUrl() !== "SuperAdmin" &&
getRoleFromUrl() !== "AdminX" &&
((item.age > 40 && formik.values.weight < 1.9) ||
(item.age > 40 && formik.values.weight > 4) ||
(item.age < 40 && formik.values.weight < 1.5) ||
(item.age < 40 && formik.values.weight > 4) ||
(item.age > 56 && formik.values.weight < 2.8) ||
(item.age > 56 && formik.values.weight > 4))
) {
openNotif({
vertical: "top",
horizontal: "center",
msg:
item?.age > 56
? "میانگین وزنی باید عددی بین 2.8 تا 4 کیلوگرم باشد!"
: item?.age > 40
? "میانگین وزنی باید عددی بین 1.9 تا 4 کیلوگرم باشد!"
: "میانگین وزنی باید عددی بین 1.5 تا 4 باشد!",
severity: "error",
});
} else {
if (isEdit) {
if (value === "native") {
dispatch(
provinceChainsEditAllocation({
chain_allcation_key: item?.key,
quantity: formik.values.quantity,
weight: parseInt(
formik.values.quantity * formik.values.weight
),
index_weight: formik.values.weight,
date: selectedDate1,
out_province: false,
driver_name: formik.values.driverName,
driver_mobile: formik.values.driverMobile,
health_code: formik.values.healthCode,
type_car: formik.values.driverCar,
pelak:
driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3],
kill_house_key: selectedKillhouse,
})
).then((r) => {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable(1);
openNotif({
vertical: "top",
horizontal: "center",
msg: "بار با موفقیت ثبت شد؛ جهت مشاهده به بخش مدیریت بارها در همین صفحه مراجعه کنید",
severity: "success",
});
}
});
} else {
dispatch(
provinceChainsEditAllocation({
chain_allcation_key: item?.key,
quantity: formik.values.quantity,
weight: parseInt(
formik.values.quantity * formik.values.weight
),
index_weight: formik.values.weight,
date: selectedDate1,
out_province: true,
driver_name: formik.values.driverName,
driver_mobile: formik.values.driverMobile,
health_code: formik.values.healthCode,
type_car: formik.values.driverCar,
pelak:
driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3],
buyer_name: formikbuyer.values.name,
buyer_mobile: formikbuyer.values.mobile,
city: formikbuyer.values.city,
province: formikbuyer.values.name,
})
).then((r) => {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable(1);
openNotif({
vertical: "top",
horizontal: "center",
msg: "بار با موفقیت ثبت شد؛ جهت مشاهده به بخش مدیریت بارها در همین صفحه مراجعه کنید",
severity: "success",
});
}
});
}
} else {
if (value === "native") {
dispatch(
provinceChainsSubmitAllocation({
hatching_key: item?.key,
quantity: formik.values.quantity,
weight: parseInt(
formik.values.quantity * formik.values.weight
),
index_weight: formik.values.weight,
date: selectedDate1,
out_province: false,
driver_name: formik.values.driverName,
driver_mobile: formik.values.driverMobile,
health_code: formik.values.healthCode,
type_car: formik.values.driverCar,
pelak:
driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3],
kill_house_key: selectedKillhouse,
})
).then((r) => {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "بار با موفقیت ثبت شد؛ جهت مشاهده به بخش مدیریت بارها در همین صفحه مراجعه کنید",
severity: "success",
});
}
});
} else {
dispatch(
provinceChainsSubmitAllocation({
hatching_key: item?.key,
quantity: formik.values.quantity,
weight: parseInt(
formik.values.quantity * formik.values.weight
),
index_weight: formik.values.weight,
date: selectedDate1,
out_province: true,
driver_name: formik.values.driverName,
driver_mobile: formik.values.driverMobile,
health_code: formik.values.healthCode,
type_car: formik.values.driverCar,
pelak:
driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3],
buyer_name: formikbuyer.values.name,
buyer_mobile: formikbuyer.values.mobile,
city: formikbuyer.values.city,
province: formikbuyer.values.name,
})
).then((r) => {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "بار با موفقیت ثبت شد؛ جهت مشاهده به بخش مدیریت بارها در همین صفحه مراجعه کنید",
severity: "success",
});
}
});
}
}
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,373 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { Button, TextField, Autocomplete, Typography } from "@mui/material";
import { provinceChainsSubmitCompany } from "../../services/province-chains-submit-company";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { provinceChainsEditCompany } from "../../services/province-chains-edit-company";
import { provinceChainsGetCompanies } from "../../services/province-chains-get-companies";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../../slaughter-house/services/slaughter-get-provinces";
export const ChainsCompaniesSubmit = ({ item }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const formik = useFormik({
initialValues: {
mobile: item?.user?.mobile ? item?.user?.mobile : "",
name: item?.name ? item?.name : "",
firstname: item?.user?.firstName ? item?.user?.firstName : "",
lastname: item?.user?.lastName ? item?.user?.lastName : "",
natioanlId: item?.user?.nationalId ? item?.user?.nationalId : "",
postal: item?.postalCode ? item?.postalCode : "",
province: item?.province ? item?.province : "",
city: item?.city ? item?.city : "",
address: item?.address ? item?.address : "",
},
validationSchema: Yup.object({
mobile: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به صورت عددی وارد کنید!")
.test("len", "شماره تلفن باید با 0 شروع شود", (val, context) => {
return context.originalValue && context.originalValue.startsWith("0");
})
.test("len", "شماره تماس 11 رقم باید باشد", (val, context) => {
if (context.originalValue) {
return context.originalValue.length === 11;
}
}),
name: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
firstname: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
lastname: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
province: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
city: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
address: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
natioanlId: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
postal: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
dispatch(slaughterGetProvinceService()).then((r) => {
setProvinceData(r.payload.data);
});
}, []);
useEffect(() => {
if (formik.values.province) {
setCityData(
[],
dispatch(slaughterGetCitiesService(formik.values.province)).then(
(r) => {
setCityData(r.payload.data);
}
)
);
}
}, [formik.values.province]);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<TextField
fullWidth
id="name"
label="نام شرکت"
variant="outlined"
value={formik.values.name}
error={formik.touched.name ? Boolean(formik.errors.name) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.name && Boolean(formik.errors.name)
? formik.errors.name
: null
}
/>
</Grid>
<Grid>
<TextField
fullWidth
id="firstname"
label="نام مدیرعامل"
variant="outlined"
value={formik.values.firstname}
error={
formik.touched.firstname ? Boolean(formik.errors.firstname) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.firstname && Boolean(formik.errors.firstname)
? formik.errors.firstname
: null
}
/>
</Grid>
<Grid>
<TextField
fullWidth
id="lastname"
label="نام خانوادگی مدیرعامل"
variant="outlined"
value={formik.values.lastname}
error={
formik.touched.lastname ? Boolean(formik.errors.lastname) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.lastname && Boolean(formik.errors.lastname)
? formik.errors.lastname
: null
}
/>
</Grid>
<Grid>
<TextField
fullWidth
id="mobile"
label="موبایل"
variant="outlined"
value={formik.values.mobile}
error={formik.touched.mobile ? Boolean(formik.errors.mobile) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.mobile && Boolean(formik.errors.mobile)
? formik.errors.mobile
: null
}
/>
</Grid>
<Grid>
<Autocomplete
style={{ width: "100%" }}
disablePortal
id="province"
options={
provinceData
? provinceData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("province", value ? value.id : "");
formik.setFieldValue("city", "");
}}
renderInput={(params) => (
<TextField {...params} label="استان را انتخاب کنید" />
)}
/>
{item && (
<Typography variant="caption" color="error">
استان: {formik.values.province}
</Typography>
)}
</Grid>
<Grid>
<Autocomplete
minWidth={210}
style={{ width: "100%" }}
disabled={!formik.values.province}
disablePortal
id="city"
options={
cityData
? cityData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("city", value ? value.id : "");
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
{item && (
<Typography variant="caption" color="error">
شهر: {formik.values.city}
</Typography>
)}
</Grid>
<Grid>
<TextField
fullWidth
id="natioanlId"
label="کد ملی"
variant="outlined"
value={formik.values.natioanlId}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.natioanlId
? Boolean(formik.errors.natioanlId)
: null
}
helperText={
formik.touched.natioanlId && Boolean(formik.errors.natioanlId)
? formik.errors.natioanlId
: null
}
/>
</Grid>
<Grid>
<TextField
fullWidth
id="address"
label="آدرس"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.address}
error={
formik.touched.address ? Boolean(formik.errors.address) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.address && Boolean(formik.errors.address)
? formik.errors.address
: null
}
/>
</Grid>
<Grid>
<TextField
fullWidth
id="postal"
label="کد پستی"
variant="outlined"
value={formik.values.postal}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.postal ? Boolean(formik.errors.postal) : null}
helperText={
formik.touched.postal && Boolean(formik.errors.postal)
? formik.errors.postal
: null
}
/>
</Grid>
</Grid>
<Grid>
<Button
variant="contained"
fullWidth
onClick={() => {
if (item) {
dispatch(
provinceChainsEditCompany({
city: formik.values.city,
province: formik.values.province,
mobile: formik.values.mobile,
first_name: formik.values.firstname,
last_name: formik.values.lastname,
national_id: formik.values.natioanlId,
address: formik.values.address,
postal_code: formik.values.postal,
name: formik.values.name,
company_key: item?.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
dispatch(provinceChainsGetCompanies());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
provinceChainsSubmitCompany({
city: formik.values.city,
province: formik.values.province,
mobile: formik.values.mobile,
first_name: formik.values.firstname,
last_name: formik.values.lastname,
national_id: formik.values.natioanlId,
address: formik.values.address,
postal_code: formik.values.postal,
name: formik.values.name,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(provinceChainsGetCompanies());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,231 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import EditIcon from "@mui/icons-material/Edit";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { ChainsCompaniesSubmit } from "../chains-companies-submit/ChainsCompaniesSubmit";
export const ProvinceChainsCompanies = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, [setSelectedDate1, setSelectedDate2]);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const isAvalableEdit = () => {
if (
getRoleFromUrl() === "AdminX" ||
getRoleFromUrl() === "SuperAdmin" ||
getRoleFromUrl() === "ProvinceOperator"
) {
return false;
} else {
return true;
}
};
const fetchApiData = async (page = 1) => {
try {
dispatch(LOADING_START());
const response = await axios.get(
`chain-company/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}&state=total_companies`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
dispatch(LOADING_END());
console.error("Error fetching data:", error);
}
};
const handlePageChange = (newPage) => {
setPage(newPage);
fetchApiData(newPage);
};
const handlePerRowsChange = (newPerPage) => {
setPerPage(newPerPage);
setPage(1);
};
useEffect(() => {
fetchApiData();
}, [selectedDate1, selectedDate2, perPage]);
useEffect(() => {
const formattedData = data?.map((item, i) => [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item.name,
item.user.firstName,
item.user.lastName,
item.user.mobile,
item.province,
item.city,
item.address,
item.postalCode,
item.requestsInfo?.numberOfRequests?.toLocaleString(),
item.requestsInfo?.totalQuantity?.toLocaleString(),
item.requestsInfo?.totalWeight?.toLocaleString(),
<Tooltip title="ویرایش اطلاعات" placement="left" key={i}>
<IconButton
disabled={isAvalableEdit()}
color="primary"
onClick={() =>
dispatch(
DRAWER({
title: "ویرایش شرکت زنجیره",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <ChainsCompaniesSubmit item={item} />,
})
)
}
>
<EditIcon />
</IconButton>
</Tooltip>,
]);
setTableData(formattedData);
}, [data, page, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
fetchApiData(1);
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid container xs={12}>
{(getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "SuperAdmin") && (
<Button
onClick={() =>
dispatch(
DRAWER({
title: "ثبت شرکت زنجیره",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <ChainsCompaniesSubmit />,
})
)
}
variant="contained"
>
ثبت شرکت زنجیره
</Button>
)}
</Grid>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={2}
>
<Grid
container
alignItems="center"
justifyContent="center"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Typography>خریداران</Typography>
<Grid container alignItems="center" gap={2}>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
<Grid>
<Tooltip title="خروجی اکسل شرکت های زنجیره">
<a
href={`${
axios.defaults.baseURL
}chain_company_buyers_excel/?role=${getRoleFromUrl()}&key=${userKey}&search=filter&value=${textValue}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"نام شرکت",
"نام مدیرعامل",
"نام خانوادگی مدیرعامل",
"موبایل",
"استان",
"شهر",
"آدرس",
"کد پستی",
"تعداد سفارشات",
"حجم سفارشات (قطعه)",
"وزن سفارشات (کیلوگرم)",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مدیریت بارها"
/>
</Grid>
);
};

View File

@@ -0,0 +1,132 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useDispatch } from "react-redux";
import { provinceGetPoultry } from "../../../aviculture/services/province-get-poultry";
import { Autocomplete, Button, TextField } from "@mui/material";
import { provinceChainsGetCompanies } from "../../services/province-chains-get-companies";
import { provinceChainsSubmitActiveChain } from "../../services/province-chains-submit-active-chain";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
export const ChainsSubmitActiveChain = ({ updateTable }) => {
const dispatch = useDispatch();
const [poultries, setPoultries] = useState([]);
const [data, setData] = useState([]);
const [dataCompany, setDataCompany] = useState([]);
const [selectedPoultries, setSelectedPoultries] = useState([]);
const [selectedCompany, setSelectedCompany] = useState("");
const [openNotif] = useContext(AppContext);
useEffect(() => {
dispatch(
provinceGetPoultry({
chain: true,
})
).then((r) => {
setData(r.payload.data);
});
dispatch(provinceChainsGetCompanies()).then((r) => {
setDataCompany(r.payload.data);
});
}, []);
useEffect(() => {
setPoultries(
data?.map((item) => {
return {
label: `${item.unitName} (${item?.user.mobile})`,
value: `${item.chainCompany?.hatchingKey}`,
};
})
);
}, [data]);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<Autocomplete
disablePortal
id="province"
multiple
options={poultries}
getOptionLabel={(option) => option.label}
onChange={(e, value) => {
setSelectedPoultries(value);
}}
renderInput={(params) => (
<TextField {...params} label="انتخاب مرغداران" />
)}
/>
</Grid>
<Grid>
<Autocomplete
disablePortal
id="province"
options={
dataCompany
? dataCompany?.map((i) => ({
id: i.key,
label: `${i.name} (${i.user.mobile})`,
}))
: []
}
onChange={(e, value) => {
setSelectedCompany(value.id);
}}
renderInput={(params) => (
<TextField {...params} label="شرکت زنجیره" />
)}
/>
</Grid>
</Grid>
<Grid>
<Button
disabled={!selectedPoultries.length || !selectedCompany}
variant="contained"
fullWidth
onClick={() => {
dispatch(
provinceChainsSubmitActiveChain({
poultry_hitchings: selectedPoultries?.map((item) => {
return item?.value;
}),
chain_company: selectedCompany,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد!",
severity: "success",
});
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
}
});
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,54 @@
import React from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs } from "@mui/material";
import { ProvinceChainsCompanies } from "../chains-companies/ProvinceChainsCompanies";
import { ChainsActiveChains } from "../chains-active-chains/ChainsActiveChains";
import { ChainsArchiveChains } from "../chain-archive-chains/ChainsArchiveChains";
import { ChainBarManagement } from "../chain-bar-management/ChainBarManagement";
export const ProvinceChains = () => {
const [value, setValue] = React.useState("0");
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Grid container justifyContent="center" xs={12}>
<Tabs
value={value}
onChange={handleChange}
textColor="secondary"
indicatorColor="secondary"
aria-label="secondary tabs example"
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
>
<Tab value="0" label="زنجیره های فعال" />
<Tab value="1" label="مدیریت بارها" />
<Tab value="2" label="شرکت ها" />
<Tab value="3" label="بایگانی" />
</Tabs>
<Grid
container
justifyContent="center"
alignItems="center"
px={2}
xs={12}
>
<Grid container justifyContent="center" mt={2} xs={12}>
{value === "0" && <ChainsActiveChains />}
</Grid>
<Grid container justifyContent="center" mt={2} xs={12}>
{value === "1" && <ChainBarManagement />}
</Grid>
<Grid container justifyContent="center" mt={2} xs={12}>
{value === "2" && <ProvinceChainsCompanies />}
</Grid>
<Grid container justifyContent="center" xs={12}>
{value === "3" && <ChainsArchiveChains />}
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,574 @@
import React, { useContext, useEffect, useState, useCallback } from "react";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import {
Button,
Box,
Typography,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from "@mui/material";
import { Add as AddIcon } from "@mui/icons-material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { updateGuildByNationalIdNewService } from "../../services/update-guild-by-national-id-new";
import { deactivateGuildService } from "../../services/deactivate-guild";
import { provinceGetCitiesService } from "../../services/province-get-cities";
import { provinceGetFieldOfWorks } from "../../services/ProvinceGetFieldOfWorks";
import { provinceGetTypeActivity } from "../../services/provinceGetTypeActivity";
import { provinceGetRegisterCodeStateService } from "../../services/province-get-register-code-state";
import { mainGetGuildsForUpdateOrCreateService } from "../../services/main-get-guilds-for-update-or-create";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { PersonalInfoSection } from "./components/PersonalInfoSection";
import { InquiryForm } from "./components/InquiryForm";
import { UpdateFromExternalButton } from "./components/UpdateFromExternalButton";
import { ConfirmationDialog } from "./components/ConfirmationDialog";
import { FormActions } from "./components/FormActions";
import { GuildInfoAccordionItem } from "./components/GuildInfoAccordionItem";
import { getValidationSchema, getInitialValues } from "./utils/formUtils";
import {
mapResponseDataToFormFields,
prepareSubmitData,
} from "./utils/dataMapping";
import { handleSubmitSuccess, handleSubmitError } from "./utils/submitHandlers";
const DeleteConfirmationDialog = ({ open, onClose, onConfirm, isDeleting }) => {
return (
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
<DialogTitle>آیا مطمئن هستید؟</DialogTitle>
<DialogContent>
<Typography variant="body2">
آیا از حذف این صنف مطمئن هستید؟ این عمل قابل بازگشت نیست.
</Typography>
</DialogContent>
<DialogActions sx={{ gap: 2, p: 2 }}>
<Button
variant="outlined"
onClick={onClose}
disabled={isDeleting}
sx={{ flex: 1 }}
>
انصراف
</Button>
<Button
variant="contained"
color="error"
onClick={onConfirm}
disabled={isDeleting}
sx={{ flex: 1 }}
>
{isDeleting ? "در حال حذف..." : "حذف"}
</Button>
</DialogActions>
</Dialog>
);
};
export const CreateGuilds = ({ guild, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasRegisterCode, setHasRegisterCode] = useState();
const [inquiryNationalCode, setInquiryNationalCode] = useState("");
const [isInquiryDone, setIsInquiryDone] = useState(false);
const [dbRegister, setDbRegister] = useState(null);
const [hasInquiry, setHasInquiry] = useState(null);
const [guildActive, setGuildActive] = useState(null);
const [guildsList, setGuildsList] = useState(() => (guild ? [guild] : []));
const [expandedAccordion, setExpandedAccordion] = useState(0);
const [guildsFormValues, setGuildsFormValues] = useState(() => {
// Initialize with guild data if editing
if (guild) {
return [getInitialValues(guild)];
}
return [];
});
const [cities, setCities] = useState([]);
const [typeActivities, setTypeActivities] = useState([]);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [deleteDialogIndex, setDeleteDialogIndex] = useState(null);
const [isDeletingGuild, setIsDeletingGuild] = useState(false);
const originalPhoneNumber = guild?.phoneNumber || null;
const currentRole = getRoleFromUrl();
const isAdmin = currentRole === "AdminX";
const isSuperAdmin = currentRole === "SuperAdmin";
const isKillHouse = currentRole === "KillHouse";
const formik = useFormik({
initialValues: getInitialValues(guild),
validationSchema: getValidationSchema(!!guild),
validateOnMount: true,
onSubmit: (values) => {
const guildsDataArray = guildsList.map((guildItem, index) => {
const guildValues = guildsFormValues[index] || values;
const combinedValues = {
...values, // Personal info (shared)
...guildValues, // Guild-specific info (overrides if same keys exist)
};
return prepareSubmitData(
combinedValues,
guildItem,
originalPhoneNumber,
hasInquiry
);
});
dispatch(updateGuildByNationalIdNewService(guildsDataArray)).then(
(result) => {
if (result.payload.error) {
handleSubmitError(openNotif, result.payload.error);
} else {
handleSubmitSuccess(
dispatch,
openNotif,
updateTable,
values,
result.payload?.data
);
}
}
);
},
});
useEffect(() => {
dispatch(provinceGetRegisterCodeStateService()).then((r) => {
const isActive = r.payload.data?.[0]?.active;
setHasRegisterCode(isActive);
if (isActive === false) {
formik.setFieldValue("isAccepted", true);
}
});
dispatch(provinceGetCitiesService()).then((r) => {
setCities(r.payload.data || []);
});
dispatch(provinceGetFieldOfWorks());
dispatch(provinceGetTypeActivity()).then((r) => {
setTypeActivities(r.payload.data || []);
});
}, []);
useEffect(() => {
formik.validateForm();
}, []);
const mapResponseToFormFields = useCallback(
(responseData) => {
const guildsData = Array.isArray(responseData.guilds)
? responseData.guilds
: [];
mapResponseDataToFormFields(responseData, inquiryNationalCode, formik);
if (responseData.dbRegister === false) {
setDbRegister(false);
setHasInquiry(null);
} else {
setHasInquiry(responseData.hasInquiry ?? null);
const firstGuild = guildsData.length > 0 ? guildsData[0] : null;
const activeStatus = firstGuild?.active ?? responseData.active ?? null;
setGuildActive(activeStatus);
formik.setFieldValue("active", activeStatus);
setDbRegister(true);
}
if (guildsData.length > 0) {
setGuildsList(guildsData);
const initialGuildValues = guildsData.map((guildItem) => {
const combinedGuild = {
...guildItem,
user: responseData.user || {},
};
return getInitialValues(combinedGuild);
});
setGuildsFormValues(initialGuildValues);
setExpandedAccordion(0);
} else {
setGuildsList([]);
setGuildsFormValues([]);
}
setTimeout(() => {
formik.validateField("mobile");
formik.validateField("national_id");
}, 0);
},
[formik, inquiryNationalCode]
);
const handleInquiry = useCallback(() => {
if (!inquiryNationalCode) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی را وارد کنید",
severity: "error",
});
return;
}
if (!isAdmin && inquiryNationalCode.length !== 10) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی 10 رقمی معتبر وارد کنید",
severity: "error",
});
return;
}
dispatch(
mainGetGuildsForUpdateOrCreateService({
national_code: inquiryNationalCode,
update: false,
})
).then((r) => {
if (r.payload.error) {
setHasInquiry(false);
if (isAdmin) {
setIsInquiryDone(true);
formik.setFieldValue("national_id", inquiryNationalCode);
}
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
if (!isAdmin) {
return;
}
}
if (r.payload.data) {
mapResponseToFormFields(r.payload.data);
setIsInquiryDone(true);
if (r.payload.data.dbRegister === false) {
setHasInquiry(true);
}
const successMsg =
r.payload.data.dbRegister === false
? "اطلاعات از سامانه خارجی دریافت شد"
: "اطلاعات از پایگاه داده دریافت شد";
openNotif({
vertical: "top",
horizontal: "center",
msg: successMsg,
severity: "success",
});
} else {
setHasInquiry(false);
setIsInquiryDone(true);
if (isAdmin) {
formik.setFieldValue("national_id", inquiryNationalCode);
}
}
});
}, [
dispatch,
inquiryNationalCode,
openNotif,
mapResponseToFormFields,
isAdmin,
formik,
]);
const handleUpdateFromExternal = useCallback(() => {
if (!formik.values.national_id || formik.values.national_id.length !== 10) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی 10 رقمی معتبر وارد کنید",
severity: "error",
});
return;
}
dispatch(
mainGetGuildsForUpdateOrCreateService({
national_code: formik.values.national_id,
update: true,
})
).then((r) => {
if (r.payload.error) {
setHasInquiry(false);
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
return;
}
if (r.payload.data) {
const updateResponse = {
...r.payload.data,
dbRegister: false,
};
mapResponseToFormFields(updateResponse);
setHasInquiry(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "اطلاعات از سامانه خارجی بروزرسانی شد",
severity: "success",
});
} else {
setHasInquiry(false);
}
});
}, [dispatch, formik.values.national_id, openNotif, mapResponseToFormFields]);
const handleAddGuild = () => {
const newIndex = guildsList.length;
setGuildsList([...guildsList, null]);
setGuildsFormValues([...guildsFormValues, getInitialValues(null)]);
setExpandedAccordion(newIndex);
};
const handleDeleteGuild = (index) => {
const guildToDelete = guildsList[index];
if (guildToDelete?.key) {
setDeleteDialogIndex(index);
setDeleteDialogOpen(true);
return;
}
if (guildsList.length > 1) {
setGuildsList(guildsList.filter((_, i) => i !== index));
setGuildsFormValues(guildsFormValues.filter((_, i) => i !== index));
if (expandedAccordion === index) {
setExpandedAccordion(0);
} else if (expandedAccordion > index) {
setExpandedAccordion(expandedAccordion - 1);
}
}
};
const handleConfirmDelete = () => {
if (deleteDialogIndex === null) return;
const guildToDelete = guildsList[deleteDialogIndex];
if (!guildToDelete?.key) return;
setIsDeletingGuild(true);
dispatch(deactivateGuildService(guildToDelete.key)).then((r) => {
setIsDeletingGuild(false);
setDeleteDialogOpen(false);
setDeleteDialogIndex(null);
if (r.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "صنف با موفقیت حذف شد",
severity: "success",
});
// Remove from list after successful deletion
if (guildsList.length > 1) {
setGuildsList(guildsList.filter((_, i) => i !== deleteDialogIndex));
setGuildsFormValues(
guildsFormValues.filter((_, i) => i !== deleteDialogIndex)
);
// If deleted accordion was expanded, expand the first one
if (expandedAccordion === deleteDialogIndex) {
setExpandedAccordion(0);
} else if (expandedAccordion > deleteDialogIndex) {
// Adjust expanded index if deleted item was before it
setExpandedAccordion(expandedAccordion - 1);
}
}
if (updateTable) {
updateTable();
}
}
});
};
const handleCloseDeleteDialog = () => {
if (!isDeletingGuild) {
setDeleteDialogOpen(false);
setDeleteDialogIndex(null);
}
};
const handleGuildValuesChange = useCallback((index, fieldName, value) => {
setGuildsFormValues((prev) => {
const newValues = [...prev];
if (!newValues[index]) {
newValues[index] = getInitialValues(null);
}
newValues[index] = {
...newValues[index],
[fieldName]: value,
};
return newValues;
});
}, []);
const handleAccordionChange = (index) => (event, isExpanded) => {
setExpandedAccordion(isExpanded ? index : false);
};
const shouldShowUpdateButton =
formik?.values?.national_id &&
(isAdmin ||
(dbRegister !== false &&
(guild ||
(!guild &&
((dbRegister === true && hasInquiry === false) ||
(isAdmin && isInquiryDone))))));
const shouldShowInquiryForm = !guild && !isInquiryDone;
const shouldShowFormContent = guild || isInquiryDone;
return (
<form onSubmit={formik.handleSubmit}>
<Grid
container
gap={SPACING.TINY}
maxHeight="80vh"
minWidth={
!guild && !isInquiryDone
? "auto"
: { xs: "96vw", md: "90vw", nlg: "1280px" }
}
overflow="auto"
p={2}
>
{shouldShowUpdateButton && (
<UpdateFromExternalButton
onUpdate={handleUpdateFromExternal}
disabled={!!formik.errors.national_id}
/>
)}
{shouldShowInquiryForm && (
<InquiryForm
inquiryNationalCode={inquiryNationalCode}
setInquiryNationalCode={setInquiryNationalCode}
onInquiry={handleInquiry}
isAdmin={isAdmin}
/>
)}
{shouldShowFormContent && (
<>
<Grid container xs={12}>
<PersonalInfoSection
formik={formik}
guild={guild}
hasInquiry={hasInquiry}
isAdmin={isAdmin}
isSuperAdmin={isSuperAdmin}
isKillHouse={isKillHouse}
/>
<Grid
item
xs={12}
lg={6}
pr={{ xs: 0, md: 2 }}
pl={{ xs: 0, md: 3 }}
>
<Grid container gap={SPACING.TINY} direction="column">
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات صنفی
</Typography>
</Grid>
<Grid item xs={12}>
{guildsList.map((guildItem, index) => (
<Box key={index} sx={{ mb: 2 }}>
<GuildInfoAccordionItem
guildIndex={index}
guildData={guildItem}
guildActive={guildActive}
isAdmin={isAdmin}
cities={cities}
typeActivities={typeActivities}
onDelete={() => handleDeleteGuild(index)}
canDelete={
(guildsList.length > 1 || !guild) && isAdmin
}
guildFormValues={guildsFormValues[index]}
onGuildValuesChange={handleGuildValuesChange}
expanded={expandedAccordion === index}
onChange={handleAccordionChange(index)}
/>
</Box>
))}
</Grid>
{isAdmin && (
<Grid item xs={12} xl={4}>
<Button
variant="contained"
color="primary"
startIcon={<AddIcon />}
onClick={handleAddGuild}
fullWidth
sx={{ mb: 2 }}
>
افزودن واحد صنفی
</Button>
</Grid>
)}
</Grid>
</Grid>
</Grid>
<Grid container item xs={12}>
{hasRegisterCode &&
(!(!guild && hasInquiry === true) ||
isAdmin ||
isSuperAdmin ||
isKillHouse) && (
<ConfirmationDialog
isAccepted={formik.values.isAccepted}
onAccept={() => formik.setFieldValue("isAccepted", true)}
onReject={() => formik.setFieldValue("isAccepted", false)}
/>
)}
<FormActions
formik={formik}
onClose={() => dispatch(CLOSE_MODAL())}
showCloseButton={
!guild &&
hasInquiry === true &&
!isAdmin &&
!isSuperAdmin &&
!isKillHouse
}
isKillHouse={isKillHouse}
onSubmit={formik.handleSubmit}
/>
</Grid>
</>
)}
</Grid>
<DeleteConfirmationDialog
open={deleteDialogOpen}
onClose={handleCloseDeleteDialog}
onConfirm={handleConfirmDelete}
isDeleting={isDeletingGuild}
/>
</form>
);
};

View File

@@ -0,0 +1,45 @@
import React from "react";
import {
Button,
Typography,
ListItem,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { DialogAlert } from "../../../../../components/dialog-alert/DialogAlert";
import { Done } from "@mui/icons-material";
export const ConfirmationDialog = ({ isAccepted, onAccept, onReject }) => {
return (
<Grid item pb={2} mt={2}>
<DialogAlert
title="تعهد نامه"
content={
<>
<ListItem>
<ListItemIcon>
<Done />
</ListItemIcon>
<ListItemText primary="لطفا صحت اطلاعات وارد شده را بررسی نمایید. پس از تکمیل و ثبت درخواست، یک کد احراز هویت از طریق پیامک برای صنف یا مباشر به شماره تلفن اعلامی ارسال میگردد." />
</ListItem>
<Typography></Typography>
</>
}
actions={
<Grid container gap={SPACING.TINY}>
<Button variant="outlined" color="error" onClick={onReject}>
رد
</Button>
<Button variant="contained" color="success" onClick={onAccept}>
موافقم
</Button>
</Grid>
}
btnTitle="تایید صحت اطلاعات"
isAccepted={isAccepted}
/>
</Grid>
);
};

View File

@@ -0,0 +1,69 @@
import React from "react";
import { Button, Typography, Checkbox, FormControlLabel } from "@mui/material";
import { Grid } from "../../../../../components/grid/Grid";
export const FormActions = ({
formik,
onClose,
showCloseButton,
isKillHouse,
onSubmit,
}) => {
if (showCloseButton) {
return (
<Grid item xs={12} mt={4}>
<Button color="primary" fullWidth variant="contained" onClick={onClose}>
متوجه شدم
</Button>
</Grid>
);
}
// For KillHouse: check if area_activity contains "مرغ"
const isAreaActivityValid = isKillHouse
? formik.values.area_activity && formik.values.area_activity.includes("مرغ")
: true;
return (
<>
<Grid item xs={12}>
<FormControlLabel
control={
<Checkbox
checked={formik.values.verify_mobile}
onChange={formik.handleChange}
name="verify_mobile"
color="primary"
/>
}
label="احراز شماره موبایل"
/>
</Grid>
<Grid item xs={12}>
<Button
disabled={
formik.errors.isAccepted ||
Boolean(formik.errors.national_id) ||
!isAreaActivityValid
}
color="primary"
fullWidth
variant="contained"
onClick={onSubmit}
type="button"
>
ثبت
</Button>
{isKillHouse && !isAreaActivityValid && (
<Typography
variant="caption"
color="error"
sx={{ mt: 1, display: "block" }}
>
رسته واحد صنفی باید شامل کلمه &quot;مرغ&quot; باشد
</Typography>
)}
</Grid>
</>
);
};

View File

@@ -0,0 +1,97 @@
import React from "react";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
IconButton,
} from "@mui/material";
import {
ExpandMore as ExpandMoreIcon,
Delete as DeleteIcon,
} from "@mui/icons-material";
import { GuildInfoSection } from "./GuildInfoSection";
export const GuildInfoAccordionItem = ({
guildIndex,
guildData,
guildActive,
isAdmin,
cities,
typeActivities,
onDelete,
canDelete,
guildFormValues,
onGuildValuesChange,
expanded,
onChange,
}) => {
// Create a formik-like object for this guild's values
const guildFormik = {
values: guildFormValues || {},
setFieldValue: (fieldName, value) => {
onGuildValuesChange(guildIndex, fieldName, value);
},
handleChange: (e) => {
onGuildValuesChange(guildIndex, e.target.name, e.target.value);
},
handleBlur: () => {},
errors: {},
touched: {},
};
const getGuildTitle = () => {
if (guildData?.guildsName) {
return guildData.guildsName;
}
if (guildData?.title) {
return guildData.title;
}
if (guildFormik.values.guild_name) {
return guildFormik.values.guild_name;
}
return `واحد صنفی ${guildIndex + 1}`;
};
return (
<Accordion expanded={expanded} onChange={onChange}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
"& .MuiAccordionSummary-content": {
alignItems: "center",
justifyContent: "space-between",
},
}}
>
<Typography variant="h6" sx={{ flexGrow: 1, fontSize: 18 }}>
{getGuildTitle()}
</Typography>
{canDelete && (
<IconButton
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
color="error"
size="small"
sx={{ mr: 1 }}
>
<DeleteIcon />
</IconButton>
)}
</AccordionSummary>
<AccordionDetails>
<GuildInfoSection
formik={guildFormik}
guild={guildData}
guildActive={guildActive}
isAdmin={isAdmin}
cities={cities}
typeActivities={typeActivities}
hideTitle={true}
noGridWrapper={true}
/>
</AccordionDetails>
</Accordion>
);
};

View File

@@ -0,0 +1,566 @@
import React from "react";
import {
TextField,
Typography,
RadioGroup,
FormControl,
Radio,
FormControlLabel,
Select,
MenuItem,
InputLabel,
Checkbox,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import BusinessIcon from "@mui/icons-material/Business";
import PublicIcon from "@mui/icons-material/Public";
import LocationCityIcon from "@mui/icons-material/LocationCity";
import DateRangeIcon from "@mui/icons-material/DateRange";
import ConfirmationNumberIcon from "@mui/icons-material/ConfirmationNumber";
import AccountBalanceIcon from "@mui/icons-material/AccountBalance";
import LocalPostOfficeIcon from "@mui/icons-material/LocalPostOffice";
import PhoneIcon from "@mui/icons-material/Phone";
import BadgeIcon from "@mui/icons-material/Badge";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import HomeIcon from "@mui/icons-material/Home";
import CorporateFareIcon from "@mui/icons-material/CorporateFare";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { LabelField } from "../../../../../components/label-field/LabelField";
import { InfoBox } from "./InfoBox";
import { STATUS_VALUES } from "../constants";
import {
convertPersianToGregorian,
convertGregorianToPersian,
} from "../utils/dateUtils";
export const GuildInfoSection = ({
formik,
guild,
guildActive,
isAdmin,
cities,
typeActivities,
hideTitle = false,
noGridWrapper = false,
}) => {
const getForeignerDisplay = (isForeigner) => {
if (isForeigner === STATUS_VALUES.NO || isForeigner === false)
return STATUS_VALUES.NO;
if (isForeigner === STATUS_VALUES.YES || isForeigner === true)
return STATUS_VALUES.YES;
return "-";
};
const getLicenseExpireDateDisplay = () => {
return formik.values.license_expire_date || "-";
};
const getActiveStatusDisplay = () => {
const activeValue =
formik.values.active !== null
? formik.values.active
: guild?.active === true || guildActive === true;
return activeValue === true ? "فعال" : "غیر فعال";
};
const content = (
<Grid container gap={SPACING.TINY} direction="column">
{!hideTitle && (
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات صنفی
</Typography>
</Grid>
)}
<Grid container xs={12}>
<Grid
container
direction="column"
xs={12}
md={6}
px={SPACING.TINY}
gap={SPACING.TINY}
>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام واحد"
variant="outlined"
fullWidth
id="guild_name"
name="guild_name"
value={formik.values.guild_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BusinessIcon}
label="نام واحد"
value={formik.values.guild_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<FormControl fullWidth>
<InputLabel id="area-activity-select-label">
رسته واحد صنفی
</InputLabel>
<Select
labelId="area-activity-select-label"
id="area_activity"
name="area_activity"
value={formik.values.area_activity || ""}
label="رسته واحد صنفی"
onChange={(e) => {
formik.setFieldValue("area_activity", e.target.value);
}}
onBlur={formik.handleBlur}
>
{typeActivities.map((activity) => (
<MenuItem key={activity.key} value={activity.title}>
{activity.title}
</MenuItem>
))}
{/* Show current value if it doesn't exist in options */}
{formik.values.area_activity &&
!typeActivities.some(
(activity) =>
activity.title === formik.values.area_activity
) && (
<MenuItem
key="current-value"
value={formik.values.area_activity}
>
{formik.values.area_activity}
</MenuItem>
)}
</Select>
</FormControl>
) : (
<InfoBox
icon={BusinessIcon}
label="رسته واحد صنفی"
value={formik.values.area_activity}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="استان"
variant="outlined"
fullWidth
id="state"
name="state"
value={formik.values.state}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PublicIcon}
label="استان"
value={formik.values.state}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<FormControl fullWidth>
<InputLabel id="city-select-label">شهرستان</InputLabel>
<Select
labelId="city-select-label"
id="city"
name="city"
value={formik.values.city || ""}
label="شهرستان"
onChange={(e) => {
formik.setFieldValue("city", e.target.value);
}}
onBlur={formik.handleBlur}
>
{cities.map((city) => (
<MenuItem key={city.key} value={city.name}>
{city.name}
</MenuItem>
))}
{/* Show current value if it doesn't exist in options */}
{formik.values.city &&
!cities.some(
(city) => city.name === formik.values.city
) && (
<MenuItem key="current-value" value={formik.values.city}>
{formik.values.city}
</MenuItem>
)}
</Select>
</FormControl>
) : (
<InfoBox
icon={LocationCityIcon}
label="شهرستان"
value={formik.values.city}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<DatePicker
label="تاریخ انقضا مجوز"
value={
formik.values.license_expire_date
? moment(
convertPersianToGregorian(
formik.values.license_expire_date
)
)
: null
}
onChange={(newValue) => {
if (newValue) {
const gregorianDate = moment(newValue).format("YYYY-MM-DD");
const persianDate =
convertGregorianToPersian(gregorianDate);
formik.setFieldValue("license_expire_date", persianDate);
} else {
formik.setFieldValue("license_expire_date", "");
}
}}
renderInput={(params) => (
<TextField {...params} fullWidth variant="outlined" />
)}
/>
) : (
<InfoBox
icon={DateRangeIcon}
label="تاریخ انقضا مجوز"
value={getLicenseExpireDateDisplay()}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره مجوز"
variant="outlined"
fullWidth
id="license_number"
name="license_number"
value={formik.values.license_number}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={ConfirmationNumberIcon}
label="شماره مجوز"
value={formik.values.license_number}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام اتحادیه"
variant="outlined"
fullWidth
id="union_name"
name="union_name"
value={formik.values.union_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={AccountBalanceIcon}
label="نام اتحادیه"
value={formik.values.union_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="وضعیت">
<FormControl fullWidth>
<RadioGroup
row
name="active"
value={
formik.values.active === true
? "true"
: formik.values.active === false
? "false"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"active",
e.target.value === "true" ? true : false
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="true"
control={<Radio />}
label="فعال"
/>
<FormControlLabel
value="false"
control={<Radio />}
label="غیر فعال"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={CheckCircleIcon}
label="وضعیت"
value={getActiveStatusDisplay()}
/>
)}
</Grid>
</Grid>
<Grid
container
xs={12}
md={6}
px={SPACING.TINY}
direction="column"
gap={SPACING.TINY}
>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="کد پستی"
variant="outlined"
fullWidth
id="postal_code"
name="postal_code"
value={formik.values.postal_code}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={LocalPostOfficeIcon}
label="کد پستی"
value={formik.values.postal_code}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره تلفن"
variant="outlined"
fullWidth
id="phone_number"
name="phone_number"
value={formik.values.phone_number}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PhoneIcon}
label="شماره تلفن"
value={formik.values.phone_number}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="آیا اتباع است؟">
<FormControl fullWidth>
<RadioGroup
row
name="is_foreigner"
value={
formik.values.is_foreigner === "بلی" ||
formik.values.is_foreigner === true
? "بلی"
: formik.values.is_foreigner === "خیر" ||
formik.values.is_foreigner === false
? "خیر"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"is_foreigner",
e.target.value === "بلی" ? "بلی" : "خیر"
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="بلی"
control={<Radio />}
label="بلی"
/>
<FormControlLabel
value="خیر"
control={<Radio />}
label="خیر"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={PublicIcon}
label="آیا اتباع است؟"
value={getForeignerDisplay(formik.values.is_foreigner)}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام شرکت"
variant="outlined"
fullWidth
id="corporation_name"
name="corporation_name"
value={formik.values.corporation_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={CorporateFareIcon}
label="نام شرکت"
value={formik.values.corporation_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شناسه ملی شرکت"
variant="outlined"
fullWidth
id="guild_national_id"
name="guild_national_id"
value={formik.values.guild_national_id}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="شناسه ملی شرکت"
value={formik.values.guild_national_id}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="وضعیت مجوز"
variant="outlined"
fullWidth
id="license_status"
name="license_status"
value={formik.values.license_status}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={CheckCircleIcon}
label="وضعیت مجوز"
value={formik.values.license_status}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="آدرس"
variant="outlined"
fullWidth
multiline
rows={3}
id="address"
name="address"
value={formik.values.address}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={HomeIcon}
label="آدرس"
value={formik.values.address}
iconSx={{ mt: 0.5 }}
/>
)}
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sx={{ display: "flex", mt: 2, pl: 2, mb: 2 }}>
{isAdmin ? (
<>
<FormControlLabel
control={
<Checkbox
checked={formik.values.steward || false}
onChange={(e) =>
formik.setFieldValue("steward", e.target.checked)
}
name="steward"
color="primary"
/>
}
label="مباشر"
/>
<FormControlLabel
sx={{ ml: 3 }}
control={
<Checkbox
checked={formik.values.guild || false}
onChange={(e) =>
formik.setFieldValue("guild", e.target.checked)
}
name="guild"
color="primary"
/>
}
label="صنف"
/>
</>
) : (
<>
<InfoBox
icon={CheckCircleIcon}
label="مباشر"
value={formik.values.steward ? "بلی" : "خیر"}
/>
<InfoBox
icon={CheckCircleIcon}
label="صنف"
value={formik.values.guild ? "بلی" : "خیر"}
sx={{ ml: 3 }}
/>
</>
)}
</Grid>
</Grid>
);
if (noGridWrapper) {
return <>{content}</>;
}
return (
<Grid item xs={12} lg={6} pr={{ xs: 0, md: 2 }} pl={{ xs: 0, md: 3 }}>
{content}
</Grid>
);
};

View File

@@ -0,0 +1,23 @@
import React from "react";
import { Box, Typography } from "@mui/material";
export const InfoBox = ({ icon: Icon, label, value, iconSx }) => (
<Box
display="flex"
alignItems={iconSx ? "flex-start" : "center"}
gap={1}
px={1.5}
py={0.5}
bgcolor="#f5f5f5"
borderRadius={1}
>
<Icon color="action" sx={iconSx} />
<Box>
<Typography variant="caption" color="text.secondary">
{label}
</Typography>
<Typography variant="body1">{value || "-"}</Typography>
</Box>
</Box>
);

View File

@@ -0,0 +1,34 @@
import React from "react";
import { TextField, Button } from "@mui/material";
export const InquiryForm = ({
inquiryNationalCode,
setInquiryNationalCode,
onInquiry,
isAdmin,
}) => {
return (
<>
<TextField
label="کد ملی برای استعلام"
variant="outlined"
fullWidth
value={inquiryNationalCode}
onChange={(e) => setInquiryNationalCode(e.target.value)}
placeholder={
isAdmin ? "کد ملی را وارد کنید" : "کد ملی 10 رقمی را وارد کنید"
}
inputProps={isAdmin ? {} : { maxLength: 10 }}
/>
<Button
color="primary"
fullWidth
variant="contained"
onClick={onInquiry}
disabled={!isAdmin && inquiryNationalCode.length !== 10}
>
استعلام
</Button>
</>
);
};

View File

@@ -0,0 +1,369 @@
import React from "react";
import {
TextField,
Typography,
RadioGroup,
FormControl,
Radio,
FormControlLabel,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import PersonIcon from "@mui/icons-material/Person";
import PhoneIcon from "@mui/icons-material/Phone";
import BadgeIcon from "@mui/icons-material/Badge";
import CakeIcon from "@mui/icons-material/Cake";
import FaceIcon from "@mui/icons-material/Face";
import LocationCityIcon from "@mui/icons-material/LocationCity";
import FavoriteIcon from "@mui/icons-material/Favorite";
import { Grid } from "../../../../../components/grid/Grid";
import { SPACING } from "../../../../../data/spacing";
import { LabelField } from "../../../../../components/label-field/LabelField";
import { InfoBox } from "./InfoBox";
import { GENDER_VALUES, ALIVE_STATUS } from "../constants";
import {
convertPersianToGregorian,
convertGregorianToPersian,
} from "../utils/dateUtils";
export const PersonalInfoSection = ({
formik,
guild,
hasInquiry,
isAdmin,
isSuperAdmin,
isKillHouse,
}) => {
const getGenderDisplay = (gender) => {
if (gender === "True" || gender === true) return GENDER_VALUES.MALE;
if (gender === "False" || gender === false) return GENDER_VALUES.FEMALE;
return "-";
};
const getAliveStatusDisplay = (isAlive) => {
if (isAlive === ALIVE_STATUS.YES || isAlive === true)
return ALIVE_STATUS.YES;
if (isAlive === ALIVE_STATUS.NO || isAlive === false)
return ALIVE_STATUS.NO;
return "-";
};
const getBirthDateDisplay = () => {
return formik.values.birth_date || "-";
};
return (
<Grid
item
xs={12}
lg={6}
pl={{ xs: 0, md: 2 }}
pr={{ xs: 0, md: 3 }}
mb={2}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
اطلاعات شخصی
</Typography>
</Grid>
<Grid container xs={12}>
<Grid container xs={12} md={6} gap={SPACING.TINY} px={SPACING.TINY}>
<Grid item xs={12}>
{guild || isAdmin ? (
<TextField
label="کد ملی"
variant="outlined"
fullWidth
id="national_id"
name="national_id"
value={formik.values.national_id}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={Boolean(formik.errors.national_id)}
helperText={formik.errors.national_id}
inputProps={{ maxLength: 10 }}
disabled={!isAdmin || isKillHouse}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="کد ملی"
value={formik.values.national_id}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام"
variant="outlined"
fullWidth
id="first_name"
name="first_name"
value={formik.values.first_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام"
value={formik.values.first_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام خانوادگی"
variant="outlined"
fullWidth
id="last_name"
name="last_name"
value={formik.values.last_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام خانوادگی"
value={formik.values.last_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شماره شناسنامه"
variant="outlined"
fullWidth
id="national_code"
name="national_code"
value={formik.values.national_code}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={BadgeIcon}
label="شماره شناسنامه"
value={formik.values.national_code}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="در قید حیات">
<FormControl fullWidth>
<RadioGroup
row
name="is_alive"
value={
formik.values.is_alive === "بلی" ||
formik.values.is_alive === true
? "بلی"
: formik.values.is_alive === "خیر" ||
formik.values.is_alive === false
? "خیر"
: ""
}
onChange={(e) => {
formik.setFieldValue(
"is_alive",
e.target.value === "بلی" ? "بلی" : "خیر"
);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="بلی"
control={<Radio />}
label="بلی"
/>
<FormControlLabel
value="خیر"
control={<Radio />}
label="خیر"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={FavoriteIcon}
label="در قید حیات"
value={getAliveStatusDisplay(formik.values.is_alive)}
/>
)}
</Grid>
</Grid>
<Grid
container
xs={12}
md={6}
gap={SPACING.TINY}
px={SPACING.TINY}
mt={{ xs: 1, md: 0 }}
>
<Grid item xs={12}>
{isAdmin ? (
<DatePicker
label="تاریخ تولد"
value={
formik.values.birth_date
? moment(
convertPersianToGregorian(formik.values.birth_date)
)
: null
}
onChange={(newValue) => {
if (newValue) {
const gregorianDate =
moment(newValue).format("YYYY-MM-DD");
const persianDate =
convertGregorianToPersian(gregorianDate);
formik.setFieldValue("birth_date", persianDate);
} else {
formik.setFieldValue("birth_date", "");
}
}}
renderInput={(params) => (
<TextField {...params} fullWidth variant="outlined" />
)}
/>
) : (
<InfoBox
icon={CakeIcon}
label="تاریخ تولد"
value={getBirthDateDisplay()}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="نام پدر"
variant="outlined"
fullWidth
id="father_name"
name="father_name"
value={formik.values.father_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={PersonIcon}
label="نام پدر"
value={formik.values.father_name}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<LabelField label="جنسیت">
<FormControl fullWidth>
<RadioGroup
row
name="gender"
value={
formik.values.gender === "True" ||
formik.values.gender === true
? "True"
: formik.values.gender === "False" ||
formik.values.gender === false
? "False"
: ""
}
onChange={(e) => {
formik.setFieldValue("gender", e.target.value);
}}
sx={{ justifyContent: "space-around" }}
>
<FormControlLabel
value="True"
control={<Radio />}
label="مرد"
/>
<FormControlLabel
value="False"
control={<Radio />}
label="زن"
/>
</RadioGroup>
</FormControl>
</LabelField>
) : (
<InfoBox
icon={FaceIcon}
label="جنسیت"
value={getGenderDisplay(formik.values.gender)}
/>
)}
</Grid>
<Grid item xs={12}>
{isAdmin ? (
<TextField
label="شهر"
variant="outlined"
fullWidth
id="person_city"
name="person_city"
value={formik.values.person_city}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
) : (
<InfoBox
icon={LocationCityIcon}
label="شهر"
value={formik.values.person_city}
/>
)}
</Grid>
<Grid item xs={12} mt={{ xs: 1, md: 0 }}>
{!guild &&
hasInquiry &&
!isAdmin &&
!isSuperAdmin &&
!isKillHouse ? (
<InfoBox
icon={PhoneIcon}
label="شماره همراه"
value={formik.values.mobile}
/>
) : isKillHouse &&
formik.values.mobile &&
/^[0-9]{11}$/.test(formik.values.mobile) ? (
<InfoBox
icon={PhoneIcon}
label="شماره همراه"
value={formik.values.mobile}
/>
) : (
<TextField
label="شماره همراه"
variant="outlined"
fullWidth
id="mobile"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
error={formik.touched.mobile && Boolean(formik.errors.mobile)}
helperText={formik.touched.mobile && formik.errors.mobile}
disabled={
isKillHouse &&
formik.values.mobile &&
/^[0-9]{11}$/.test(formik.values.mobile)
}
/>
)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,19 @@
import React from "react";
import { Button } from "@mui/material";
import { Refresh } from "@mui/icons-material";
import { Grid } from "../../../../../components/grid/Grid";
export const UpdateFromExternalButton = ({ onUpdate, disabled }) => {
return (
<Grid container xs={12} justifyContent="flex-end">
<Button
onClick={onUpdate}
disabled={disabled}
color="primary"
size="small"
>
بروزرسانی از سامانه واحد <Refresh />
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,17 @@
export const STATUS_VALUES = {
YES: "بلی",
NO: "خیر",
TRUE: "True",
FALSE: "False",
};
export const GENDER_VALUES = {
MALE: "مرد",
FEMALE: "زن",
};
export const ALIVE_STATUS = {
YES: "بلی",
NO: "خیر",
};

View File

@@ -0,0 +1,232 @@
import { getRoleFromUrl } from "../../../../../utils/getRoleFromUrl";
import { normalizeExternalApiDate, normalizeDatabaseDate } from "./dateUtils";
import { formatDateForSubmit } from "./dateUtils";
export const prepareSubmitData = (
values,
guild,
originalPhoneNumber,
hasInquiry
) => {
const baseData = {
national_id: values.national_id,
mobile: values.mobile,
mobilenumber: values.mobile,
steward: !!values.steward,
guild: !!values.guild,
active_register_code: !!values.verify_mobile,
firstName: values.first_name || "",
lastName: values.last_name || "",
fatherName: values.father_name || "",
gender: values.gender || "",
identityNo: values.national_code || "",
isLive: values.is_alive === "بلی" || values.is_alive === true,
birthDate: formatDateForSubmit(
values.birth_date || "",
hasInquiry === true
),
city: values.city || "",
address: values.address || "",
postalcode: values.postal_code || "",
licenseNumber: values.license_number || "",
licenseExpireDate: formatDateForSubmit(
values.license_expire_date || "",
hasInquiry === true
),
licenseIssueDate: formatDateForSubmit(
values.license_issue_date || "",
hasInquiry === true
),
licenseType: values.license_type || "",
licenseStatus: values.license_status || "",
isicname: values.area_activity || "",
corporationName: values.corporation_name || "",
nationalId: values.guild_national_id || "",
unionName: values.union_name || "",
phonenumber: values.phone_number || "",
hasPartner: values.has_partner === true || values.has_partner === "بلی",
isForeigner: values.is_foreigner === true || values.is_foreigner === "بلی",
title: values.guild_name || "",
role: getRoleFromUrl(),
has_inquiry: hasInquiry !== null ? hasInquiry : false,
...(values.active !== null && { active: values.active }),
};
if (guild) {
baseData.guilds_key = guild.key;
if (values.mobile !== originalPhoneNumber) {
baseData.mobile = values.mobile;
baseData.mobilenumber = values.mobile;
}
}
return baseData;
};
export const mapResponseDataToFormFields = (
responseData,
inquiryNationalCode,
formik
) => {
const isExternalApi = responseData.dbRegister === false;
// New structure: user is at top level, guilds is an array
const userData = responseData.user || {};
// For personal info, we use the first guild's data if available, or empty
const firstGuild =
Array.isArray(responseData.guilds) && responseData.guilds.length > 0
? responseData.guilds[0]
: {};
const guildData = firstGuild || {};
const layerTwo = guildData.layerTwo || {};
const addressData = firstGuild?.address || guildData.address || {};
const provinceData = addressData.province || {};
const cityData = addressData.city || {};
const nationalIdValue = isExternalApi
? String(
layerTwo.nationalcode || userData.nationalCode || inquiryNationalCode
).trim()
: String(userData.nationalId || inquiryNationalCode).trim();
const birthDatePersian = isExternalApi
? normalizeExternalApiDate(userData.birthDate || "")
: normalizeDatabaseDate(userData.birthday || "");
const licenseExpireDatePersian = isExternalApi
? normalizeExternalApiDate(guildData.licenseExpireDate || "")
: normalizeDatabaseDate(firstGuild.licenseExpireDate || "");
const licenseIssueDatePersian = isExternalApi
? normalizeExternalApiDate(layerTwo.licenseIssueDate || "")
: normalizeDatabaseDate(responseData.licenseIssueDate || "");
const genderValue = isExternalApi
? userData.gender === true
? "True"
: userData.gender === false
? "False"
: ""
: userData.gender || "";
const isAliveValue = isExternalApi
? userData.isLive === true
? "بلی"
: userData.isLive === false
? "خیر"
: ""
: userData.isAlive === false
? "خیر"
: userData.isAlive === true
? "بلی"
: "";
const isForeignerValue = isExternalApi
? layerTwo.isForeigner === "خیر"
? false
: layerTwo.isForeigner === "بلی"
? true
: ""
: responseData.isForeignNational === false
? false
: responseData.isForeignNational === true
? true
: "";
const hasStewardValue = isExternalApi
? layerTwo.hasSteward === "خیر"
? false
: layerTwo.hasSteward === "بلی"
? true
: ""
: responseData.steward === false
? false
: responseData.steward === true
? true
: "";
const hasPartnerValue = isExternalApi
? layerTwo.hasPartner === "خیر"
? false
: layerTwo.hasPartner === "بلی"
? true
: ""
: responseData.hasPartner === false
? false
: responseData.hasPartner === true
? true
: "";
const values = {
first_name: userData.firstName || "",
last_name: userData.lastName || "",
national_id: nationalIdValue,
national_code: isExternalApi
? userData.identityNo || ""
: userData.nationalCode || "",
birth_date: birthDatePersian,
father_name: userData.fatherName || "",
gender: genderValue,
person_city: userData.city || "",
is_alive: isAliveValue,
// Guild fields - will be set per guild in accordion, so we set empty or first guild's data
guild_name: isExternalApi
? guildData.title || ""
: firstGuild.guildsName || "",
area_activity: isExternalApi
? guildData.isicname || ""
: firstGuild.areaActivity || "",
state: isExternalApi ? guildData.state || "" : provinceData.name || "",
city: isExternalApi ? guildData.city || "" : cityData.name || "",
address: isExternalApi
? guildData.address || ""
: addressData.address || "",
license_expire_date: licenseExpireDatePersian,
license_status: isExternalApi
? guildData.licenseStatus || ""
: firstGuild.licenseStatus || "",
license_type: isExternalApi
? guildData.licenseType || ""
: firstGuild.licenseType || "",
license_number: isExternalApi
? guildData.licenseNumber || ""
: firstGuild.licenseNumber || "",
union_name: isExternalApi
? layerTwo.unionName || ""
: firstGuild.unionName || "",
postal_code: isExternalApi
? layerTwo.postalcode || ""
: addressData.postalCode || "",
phone_number: isExternalApi
? layerTwo.phonenumber || ""
: firstGuild.phoneNumber || "",
mobile: isExternalApi ? layerTwo.mobilenumber || "" : userData.mobile || "",
guild_national_id: isExternalApi
? layerTwo.nationalId || ""
: firstGuild.nationalCode || "",
is_foreigner: isForeignerValue,
corporation_name: isExternalApi
? layerTwo.corporationName || ""
: firstGuild.companyName || "",
has_steward: hasStewardValue,
has_partner: hasPartnerValue,
steward: isExternalApi ? false : firstGuild.isSteward || false,
guild: isExternalApi
? typeof guildData.guild === "boolean"
? guildData.guild
: false
: typeof firstGuild.guild === "boolean"
? firstGuild.guild
: false,
license_issue_date: licenseIssueDatePersian,
...(isExternalApi
? {}
: {
company_name: firstGuild.companyName || "",
company_identifier: firstGuild.companyIdentifier || "",
type_activity_name: firstGuild.typeActivity || "",
}),
};
formik.setValues({ ...formik.values, ...values });
};

View File

@@ -0,0 +1,158 @@
import {
convertToIranianTime,
convertPersianToEnglishNumerals,
} from "../../../../../utils/formatTime";
import { fromJalali } from "../../../../../utils/jalali";
export const convertGregorianToPersian = (gregorianDateString) => {
if (!gregorianDateString || typeof gregorianDateString !== "string") {
return "";
}
// Check if the date is already in Persian format (YYYY/MM/DD with year < 1500)
const persianPattern = /^\d{4}\/\d{2}\/\d{2}$/;
if (persianPattern.test(gregorianDateString)) {
const year = parseInt(gregorianDateString.split("/")[0]);
// If year is < 1500, it's likely Persian, return as is
if (year < 1500) {
return gregorianDateString;
}
}
// Try to convert using convertToIranianTime
try {
return convertToIranianTime(gregorianDateString);
} catch (error) {
console.error("Error converting Gregorian date to Persian:", error);
return gregorianDateString; // Return original on error
}
};
export const convertPersianToGregorian = (persianDateString) => {
if (!persianDateString || typeof persianDateString !== "string") {
return "";
}
const normalizedDateString =
convertPersianToEnglishNumerals(persianDateString);
const gregorianPattern = /^\d{4}[-/]\d{2}[-/]\d{2}$/;
if (gregorianPattern.test(normalizedDateString)) {
const year = parseInt(normalizedDateString.split(/[-/]/)[0]);
if (year > 1500) {
return normalizedDateString.replace(/\//g, "-");
}
}
const parts = normalizedDateString.split("/");
if (parts.length !== 3) {
return persianDateString;
}
const py = parseInt(parts[0]);
const pm = parseInt(parts[1]);
const pd = parseInt(parts[2]);
if (isNaN(py) || isNaN(pm) || isNaN(pd)) {
return persianDateString;
}
try {
const gregorianDate = fromJalali(py, pm, pd);
const year = gregorianDate.getFullYear();
const month = String(gregorianDate.getMonth() + 1).padStart(2, "0");
const day = String(gregorianDate.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
} catch (error) {
console.error("Error converting Persian date to Gregorian:", error);
return persianDateString;
}
};
export const normalizeExternalApiDate = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
return convertPersianToEnglishNumerals(dateString);
};
export const normalizeDatabaseDate = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
const first10Chars = dateString.substring(0, 10);
const normalizedDate = first10Chars.replace(/-/g, "/");
return convertToIranianTime(normalizedDate);
};
export const formatDateForSubmitExternal = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
return convertPersianToEnglishNumerals(dateString);
};
export const formatDateForSubmitDatabase = (dateString) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
const normalizedDate = convertPersianToEnglishNumerals(dateString);
const persianPattern = /^\d{4}\/\d{2}\/\d{2}$/;
if (persianPattern.test(normalizedDate)) {
const year = parseInt(normalizedDate.split("/")[0]);
if (year < 1500) {
const parts = normalizedDate.split("/");
const py = parseInt(parts[0]);
const pm = parseInt(parts[1]);
const pd = parseInt(parts[2]);
if (!isNaN(py) && !isNaN(pm) && !isNaN(pd)) {
try {
const gregorianDate = fromJalali(py, pm, pd);
const gy = gregorianDate.getFullYear();
const gm = String(gregorianDate.getMonth() + 1).padStart(2, "0");
const gd = String(gregorianDate.getDate()).padStart(2, "0");
return `${gy}/${gm}/${gd}`;
} catch (error) {
console.error("Error converting Persian to Gregorian:", error);
}
}
}
}
const gregorianPattern = /^\d{4}[-/]\d{2}[-/]\d{2}$/;
if (gregorianPattern.test(normalizedDate)) {
const year = parseInt(normalizedDate.split(/[-/]/)[0]);
if (year > 1900) {
return normalizedDate.replace(/-/g, "/");
}
}
try {
const date = new Date(normalizedDate);
if (!isNaN(date.getTime())) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}/${month}/${day}`;
}
} catch (error) {
console.error("Error formatting database date:", error);
}
return normalizedDate.replace(/-/g, "/");
};
export const formatDateForSubmit = (dateString, isExternalApi = false) => {
if (!dateString || typeof dateString !== "string") {
return "";
}
if (isExternalApi) {
return formatDateForSubmitExternal(dateString);
} else {
return formatDateForSubmitDatabase(dateString);
}
};

View File

@@ -0,0 +1,95 @@
import * as yup from "yup";
import { normalizeDatabaseDate } from "./dateUtils";
export const getValidationSchema = (isEditMode) =>
yup.object({
national_id: yup
.string()
.required("کد ملی الزامی است")
.matches(/^[0-9]{10}$/, "کد ملی باید 10 رقم باشد"),
mobile: isEditMode
? yup
.string()
.nullable()
.test(
"mobile-format",
"شماره تلفن باید 11 رقم باشد",
(value) => !value || /^[0-9]{11}$/.test(value)
)
: yup
.string()
.required("شماره تلفن الزامی است")
.matches(/^[0-9]{11}$/, "شماره تلفن باید 11 رقم باشد"),
first_name: yup.string(),
last_name: yup.string(),
guild_name: yup.string(),
guild_category: yup.string(),
state: yup.string(),
city: yup.string(),
address: yup.string(),
license_expire_date: yup.string(),
license_status: yup.string(),
union_name: yup.string(),
postal_code: yup.string(),
guild_national_id: yup.string(),
is_foreigner: yup.string(),
national_code: yup.string(),
has_steward: yup.string(),
has_partner: yup.string(),
license_number: yup.string(),
isAccepted: yup
.boolean()
.test("req", "باید تعهد نامه را بپذیرید!", (val) => {
return val === true;
})
.required("این فیلد اجباری است!"),
});
export const getInitialValues = (guild) => ({
first_name: guild?.user?.firstName || "",
last_name: guild?.user?.lastName || "",
corporation_name: guild?.companyName || "",
national_id: guild?.user?.nationalId || "",
national_code: guild?.user?.nationalCode || "",
birth_date: normalizeDatabaseDate(guild?.user?.birthday || ""),
father_name: guild?.user?.fatherName || "",
gender: guild?.user?.gender || "",
person_city: guild?.user?.city || "",
is_alive: guild?.user?.isAlive || "",
guild_name: guild?.guildsName || "",
area_activity: guild?.areaActivity || "",
state: guild?.address?.province?.name || "",
city: guild?.address?.city?.name || "",
address: guild?.address?.address || "",
license_expire_date: normalizeDatabaseDate(guild?.licenseExpireDate || ""),
license_status: guild?.licenseStatus || "",
license_type: guild?.licenseType || "",
union_name: guild?.unionName || "",
postal_code: guild?.address?.postalCode || "",
phone_number: guild?.phoneNumber || "",
mobile: guild?.user?.mobile || "",
is_foreigner: guild?.is_foreign_national || "",
has_steward: guild?.hasSteward || "",
has_partner: guild?.hasPartner || "",
license_number: guild?.licenseNumber || "",
isAccepted: guild?.provinceAcceptState === "accepted" || false,
steward:
typeof guild?.steward === "boolean"
? guild.steward
: typeof guild?.isSteward === "boolean"
? guild.isSteward
: false,
guild:
typeof guild?.guild === "boolean"
? guild.guild
: typeof guild?.isGuild === "boolean"
? guild.isGuild
: false,
verify_mobile: guild?.verifyMobile || false,
guild_national_id: guild?.nationalId || "",
license_issue_date: normalizeDatabaseDate(guild?.licenseIssueDate || ""),
company_name: guild?.companyName || "",
company_identifier: guild?.companyIdentifier || "",
type_activity_name: guild?.typeActivityName || "",
active: guild?.active ?? null,
});

View File

@@ -0,0 +1,45 @@
import {
CLOSE_MODAL,
OPEN_MODAL,
} from "../../../../../lib/redux/slices/appSlice";
import { ProvinceManageGuildsSubmitRegisterCode } from "../../province-manage-guilds-submit-register-code/ProvinceManageGuildsSubmitRegisterCode";
export const handleSubmitSuccess = (
dispatch,
openNotif,
updateTable,
values,
responseData
) => {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
if (values.verify_mobile && responseData) {
dispatch(
OPEN_MODAL({
title: "ثبت کد احراز",
content: (
<ProvinceManageGuildsSubmitRegisterCode
item={responseData}
updateTable={updateTable}
/>
),
})
);
}
};
export const handleSubmitError = (openNotif, error) => {
openNotif({
vertical: "top",
horizontal: "center",
msg: error,
severity: "error",
});
};

View File

@@ -0,0 +1,81 @@
import { Autocomplete, Button, TextField } from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as yup from "yup";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { provinceCreateStewardService } from "../../services/province-create-steward";
import { provinceGetGuildsService } from "../../services/province-get-guilds";
const validationSchema = yup.object({
// guild: yup.string().required(""),
});
export const CreateStewards = ({ guild }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const { provinceGetGuildsOptions } = useSelector(
(state) => state.provinceSlice
);
useEffect(() => {
dispatch(provinceGetGuildsService());
}, []);
const formik = useFormik({
initialValues: {
guild: null,
},
validationSchema,
onSubmit: (values) => {
// Handle form submission here
dispatch(
provinceCreateStewardService({ guilds_key: values.guild.value })
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.TINY}>
<Autocomplete
style={{ width: "100%" }}
options={provinceGetGuildsOptions}
value={formik.values.guild}
onChange={(e, v) => {
formik.setFieldValue("guild", v);
}}
error={formik.touched.guild && Boolean(formik.errors.guild)}
helperText={formik.touched.guild && formik.errors.guild}
getOptionLabel={(option) => option.label}
renderInput={(params) => (
<TextField {...params} label="انتخاب صنف" variant="outlined" />
)}
/>
<Button color="primary" fullWidth variant="contained" type="submit">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,50 @@
import { Autocomplete, Button, TextField } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { getCitiesService } from "../../services/get-cities";
import { provinceEditPoultryCityService } from "../../services/province-edit-poultry-city";
export const EditPoultryCity = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [sellected, setSellected] = useState();
const { getCitiesOptions } = useSelector((state) => state.provinceSlice);
const getOptionLabel = (option) => option.label; // Customize how the label is displayed
useEffect(() => {
dispatch(getCitiesService());
}, []);
return (
<Grid container gap={1} width="100%">
<Autocomplete
options={getCitiesOptions}
getOptionLabel={getOptionLabel}
sx={{ width: "100%" }}
onChange={(event, value) => setSellected(value)}
value={sellected}
renderInput={(params) => <TextField {...params} label="تعاونی" />}
/>
<Button
fullWidth
variant="contained"
onClick={() => {
if (sellected) {
dispatch(
provinceEditPoultryCityService({
key: item.key,
city_operator: sellected.value,
})
).then((r) => {
dispatch(CLOSE_MODAL());
updateTable();
});
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,167 @@
import {
Autocomplete,
Button,
Checkbox,
FormControlLabel,
InputAdornment,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { provinceEditStewardService } from "../../services/province-edit-steward";
import { provinceGetBuyersService } from "../../services/province-get-buyers";
const validationSchema = Yup.object().shape({
// agreed: Yup.boolean().oneOf([true], 'You must agree to the terms and conditions'),
});
export const EditStewards = ({ guild }) => {
const { provinceGetBuyersOptions } = useSelector(
(state) => state.provinceSlice
);
const [warehouses, setWarehouses] = useState(
guild.centersAllocation ? guild.centersAllocation : []
);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
allocation_limit: guild.allocationLimit,
centers_allocation: "",
limitation_allocation: guild.limitationAllocation,
},
validationSchema,
onSubmit: (values) => {
dispatch(
provinceEditStewardService({
allocation_limit: values.allocation_limit,
limitation_allocation: values.limitation_allocation,
centers_allocation: warehouses,
steward_key: guild.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
useEffect(() => {
dispatch(provinceGetBuyersService());
}, []);
return (
<form onSubmit={formik.handleSubmit} style={{ width: "100%" }}>
<Grid container direction="column" width="100%" gap={SPACING.SMALL}>
<FormControlLabel
control={
<Checkbox
name="limitation_allocation"
checked={formik.values.limitation_allocation}
onChange={formik.handleChange}
/>
}
label="محدودیت تخصیص"
/>
{formik.errors.limitation_allocation && (
<div>{formik.errors.limitation_allocation}</div>
)}
{formik.values.limitation_allocation && (
<>
<TextField
label="حداکثر تخصیص"
variant="outlined"
fullWidth
id="allocation_limit"
name="allocation_limit"
InputProps={{
endAdornment: (
<InputAdornment position="start">کیلوگرم</InputAdornment>
),
}}
value={formik.values.allocation_limit}
onChange={formik.handleChange}
error={
formik.touched.allocation_limit &&
Boolean(formik.errors.allocation_limit)
}
helperText={
formik.touched.allocation_limit &&
formik.errors.allocation_limit
}
/>
<Autocomplete
style={{ width: "100%" }}
options={provinceGetBuyersOptions}
value={formik.values.guild}
onChange={(e, item) => {
setWarehouses([...warehouses, item]);
}}
error={formik.touched.guild && Boolean(formik.errors.guild)}
helperText={formik.touched.guild && formik.errors.guild}
getOptionLabel={(option) => option.label}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب انبار"
variant="outlined"
/>
)}
/>
<Typography>محدودیت انبار های انتخاب شده</Typography>
{!warehouses.length && (
<Typography variant="caption">محدودیتی وجود ندارد</Typography>
)}
{warehouses?.map((item, i) => {
return (
<Grid
container
gap={SPACING.SMALL}
key={item.value}
alignItems="center"
>
<Button
variant="text"
onClick={() => {
setWarehouses(
warehouses.filter((it) => it.value !== item.value)
);
}}
>
حذف
</Button>
<Typography key={item.label}>{item.label}</Typography>
</Grid>
);
})}
</>
)}
<Button color="primary" fullWidth variant="contained" type="submit">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,311 @@
import {
Button,
Pagination,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import axios from "axios";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { PageTable } from "../../../../components/page-table/PageTable";
import { SPACING } from "../../../../data/spacing";
import { AppContext } from "../../../../contexts/AppContext";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { format } from "date-fns-jalali";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
export const FailedTransactions = () => {
const authToken = useSelector((state) => state.userSlice.authToken);
console.log(authToken);
const userInfo = useSelector((state) => state.userSlice);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [dataTableM, setDataTableM] = useState([]);
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const fetchApiData = async (page, textValue) => {
setLoading(true);
const response = await axios.get(
`transactions/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}&date1=${selectedDate1}&date2=${selectedDate2}&state=failed&role=${getRoleFromUrl()}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
const handlePerRowsChange = async (newPerPage, page) => {
setLoading(true);
const response = await axios.get(
`transactions/?search=filter&value=${textValue}&page=${page}&page_size=${newPerPage}&date1=${selectedDate1}&date2=${selectedDate2}&state=failed&role=${getRoleFromUrl()}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
dispatch(LOADING_END());
};
const [page, setPage] = useState(0);
const handleChangePageM = (event, newPage) => {
dispatch(LOADING_START());
setPage(newPage);
fetchApiData(newPage + 1, textValue);
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`transactions/?search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&state=failed&role=${getRoleFromUrl()}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
const columns = [
{
name: "تاریخ و زمان",
selector: (item) => {
return format(new Date(item.createDate), "yyyy/MM/dd hh:mm:ss");
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "پرداخت کننده",
selector: (item) => {
return item.payer;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "شماره درخواست",
selector: (item) => {
return item.orderId;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "شماره پیگیری",
selector: (item) => {
return item.saleReferenceId;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "کدسفارش",
selector: (item) => {
return item.orderId;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "شماره کارت",
selector: (item) => {
return item.cardHolderPan;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "قیمت",
selector: (item) => {
return item?.amount?.toLocaleString() + " ﷼";
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "متن خطا",
selector: (item) => {
return item.message?.split("_")?.join(" ");
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
];
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>تراکنش های ناموفق</Typography>
<Grid container gap={SPACING.SMALL}>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}unsuccessful_transactions_excel/?date1=${selectedDate1}&date2=${selectedDate2}&key=${userInfo?.userProfile?.key}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
);
const columnNames = columns.map((column) => column.name);
const isMobile = window.innerWidth <= 600;
useEffect(() => {
const d = data?.map((item) => {
return [
format(new Date(item.createDate), "yyyy/MM/dd hh:mm:ss"),
item.payerInfo,
item.refId,
item.message?.split("_")?.join(" "),
];
});
setDataTableM(d);
}, [data]);
return (
<Grid container justifyContent="center" gap={SPACING.SMALL}>
{isMobile ? (
<>
{tableTitle}
<SimpleTable columns={columnNames} data={dataTableM} />
<Pagination
count={Math.ceil(totalRows / 10)}
page={page + 1}
variant="outlined"
onChange={(event, newPage) => {
handleChangePageM(event, newPage - 1);
}}
/>
</>
) : (
<PageTable
title={tableTitle}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
)}
</Grid>
);
};

View File

@@ -0,0 +1,257 @@
import {
Autocomplete,
Button,
Checkbox,
FormControlLabel,
InputAdornment,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceChangeActiveGuildService } from "../../services/province-change-active-guild";
import { provinceGetBuyersService } from "../../services/province-get-buyers";
import { provinceGetStewardsService } from "../../services/province-get-stewards";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const validationSchema = Yup.object().shape({
// agreed: Yup.boolean().oneOf([true], 'You must agree to the terms and conditions'),
});
export const GuildLimitaion = ({ guild, updateTable }) => {
const { provinceGetStewardsOptions, provinceGetBuyersOptions } = useSelector(
(state) => state.provinceSlice
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [warehouses, setWarehouses] = useState(
guild.centersAllocation ? guild.centersAllocation : []
);
const [buyers, setBuyers] = useState(
guild.killHouseCentersAllocation ? guild.killHouseCentersAllocation : []
);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
allocation_limit: guild.allocationLimit,
centers_allocation: "",
limitation_allocation: guild.limitationAllocation,
currentBuyer: "",
},
validationSchema,
onSubmit: (values) => {
dispatch(
provinceChangeActiveGuildService({
kill_house_centers_allocation: buyers,
allocation_limit: values.allocation_limit,
limitation_allocation: values.limitation_allocation,
centers_allocation: warehouses,
guilds_key: guild.key,
})
).then((r) => {
updateTable();
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
useEffect(() => {
dispatch(
provinceGetStewardsService({
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
provinceGetBuyersService({
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedSubUser?.key]);
return (
<form onSubmit={formik.handleSubmit} style={{ width: "100%" }}>
<Grid container direction="column" width="100%" gap={SPACING.SMALL}>
<FormControlLabel
control={
<Checkbox
name="limitation_allocation"
checked={formik.values.limitation_allocation}
onChange={formik.handleChange}
/>
}
label="محدودیت تخصیص"
/>
{formik.errors.limitation_allocation && (
<div>{formik.errors.limitation_allocation}</div>
)}
{formik.values.limitation_allocation && (
<>
<TextField
label="حداکثر تخصیص"
variant="outlined"
fullWidth
id="allocation_limit"
name="allocation_limit"
InputProps={{
endAdornment: (
<InputAdornment position="start">کیلوگرم</InputAdornment>
),
}}
value={formik.values.allocation_limit}
onChange={formik.handleChange}
error={
formik.touched.allocation_limit &&
Boolean(formik.errors.allocation_limit)
}
helperText={
formik.touched.allocation_limit &&
formik.errors.allocation_limit
}
/>
{!guild?.steward && getRoleFromUrl() !== "Guilds" && (
<>
<Autocomplete
style={{ width: "100%" }}
options={provinceGetStewardsOptions}
value={formik.values.guild}
onChange={(e, item) => {
setWarehouses([...warehouses, item]);
}}
error={formik.touched.guild && Boolean(formik.errors.guild)}
helperText={formik.touched.guild && formik.errors.guild}
getOptionLabel={(option) => option.label}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب مباشر"
variant="outlined"
/>
)}
/>
<Typography>محدودیت مباشرین انتخاب شده</Typography>
{!warehouses.length && (
<Typography variant="caption">محدودیتی وجود ندارد</Typography>
)}
{warehouses?.map((item) => {
return (
<>
{item !== null && (
<Grid
container
gap={SPACING.SMALL}
key={item.value}
alignItems="center"
>
<Button
variant="text"
onClick={() => {
setWarehouses(
warehouses.filter(
(it) => it.value !== item.value
)
);
}}
>
حذف
</Button>
<Typography key={item.label}>{item.label}</Typography>
</Grid>
)}
</>
);
})}
</>
)}
</>
)}
{formik.values.limitation_allocation &&
getRoleFromUrl() !== "KillHouse" &&
getRoleFromUrl() !== "Guilds" && (
<>
<Autocomplete
style={{ width: "100%" }}
options={provinceGetBuyersOptions}
value={formik.values.guild}
onChange={(e, item) => {
setBuyers([...buyers, item]);
}}
error={formik.touched.guild && Boolean(formik.errors.guild)}
helperText={formik.touched.guild && formik.errors.guild}
getOptionLabel={(option) => option.label}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب انبار"
variant="outlined"
/>
)}
/>
<Typography>محدودیت انبار های انتخاب شده</Typography>
{!buyers.length && (
<Typography variant="caption">محدودیتی وجود ندارد</Typography>
)}
{buyers?.map((item) => {
return (
<>
{item !== null && (
<Grid
container
gap={SPACING.SMALL}
key={item.value}
alignItems="center"
>
<Button
variant="text"
onClick={() => {
setBuyers(
buyers.filter((it) => it.value !== item.value)
);
}}
>
حذف
</Button>
<Typography key={item.label}>{item.label}</Typography>
</Grid>
)}
</>
);
})}
</>
)}
<Button color="primary" fullWidth variant="contained" type="submit">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,237 @@
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { useLocation } from "react-router-dom";
import {
// ROUTE_ADMINX_ROUTE_GUILDS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS_REQUESTS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_ADMINX_ROUTE_MANAGE_GUILDS,
ROUTE_ADMINX_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS_REQUESTS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_MANAGE_GUILDS,
ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_GUILDS,
// ROUTE_SUPER_ADMIN_ROUTE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS_REQUESTS,
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_MANAGE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_TRUE_GUILDS,
ROUTE_ADMINX_ROUTE_OUT_PROVINCE_TRUE_GUILDS,
ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_TRUE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_LEGAL_GUILDS,
ROUTE_ADMINX_ROUTE_OUT_PROVINCE_LEGAL_GUILDS,
ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_LEGAL_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_TRUE_GUILDS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_TRUE_GUILDS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_TRUE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_LEGAL_GUILDS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_LEGAL_GUILDS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_LEGAL_GUILDS,
} from "../../../../routes/routes";
import LinkItem from "../../../../components/link-item/LinkItem";
import { MdCorporateFare } from "react-icons/md";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { BackButton } from "../../../../components/back-button/BackButton";
export const GuildsOperations = () => {
const { pathname } = useLocation();
return (
<>
{[
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_ADMINX_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_GUILDS,
].includes(pathname) && <BackButton />}
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={"row"}
justifyContent="center"
xs={12}
>
{[
ROUTE_SUPER_ADMIN_ROUTE_MANAGE_GUILDS,
ROUTE_ADMINX_ROUTE_MANAGE_GUILDS,
ROUTE_PROVINCE_ROUTE_MANAGE_GUILDS,
].includes(pathname) && (
<>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS
: ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف داخل استان"
/>
</NavLink>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_OUT_PROVINCE_GUILDS
: ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف خارج استان"
/>
</NavLink>
</>
)}
{[
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS,
].includes(pathname) && (
<>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS_REQUESTS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS_REQUESTS
: ROUTE_PROVINCE_ROUTE_IN_PROVINCE_GUILDS_REQUESTS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="درخواست های ثبت صنف"
/>
</NavLink>
{/* <NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_GUILDS
: ROUTE_PROVINCE_ROUTE_GUILDS
}
active={pathname === ROUTE_PROVINCE_ROUTE_GUILDS ? "true" : null}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف"
/>
</NavLink> */}
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_TRUE_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_IN_PROVINCE_TRUE_GUILDS
: ROUTE_PROVINCE_ROUTE_IN_PROVINCE_TRUE_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف حقیقی"
/>
</NavLink>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_LEGAL_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_IN_PROVINCE_LEGAL_GUILDS
: ROUTE_PROVINCE_ROUTE_IN_PROVINCE_LEGAL_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف حقوقی"
/>
</NavLink>
{/* <NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_STEWARDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_IN_PROVINCE_STEWARDS
: ROUTE_PROVINCE_ROUTE_IN_PROVINCE_STEWARDS
}
active={pathname === ROUTE_PROVINCE_ROUTE_GUILDS ? "true" : null}
>
<LinkItem
icon={<IoIosPeople size={30} color="#244CCC" />}
title="مباشرین"
/>
</NavLink>
{(getRoleFromUrl() === "AdminX" ||
getRoleFromUrl() === "SuperAdmin") && (
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS_DISTRIBUTIONS
: ROUTE_ADMINX_ROUTE_IN_PROVINCE_GUILDS_DISTRIBUTIONS
}
active={
pathname ===
ROUTE_SUPER_ADMIN_ROUTE_IN_PROVINCE_GUILDS_DISTRIBUTIONS
? "true"
: null
}
>
<LinkItem
icon={<VscLiveShare size={30} color="#244CCC" />}
title="مدیریت مباشرین"
/>
</NavLink>
)} */}
</>
)}
{[
ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_ADMINX_ROUTE_OUT_PROVINCE_GUILDS,
ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_GUILDS,
].includes(pathname) && (
<>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_TRUE_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_OUT_PROVINCE_TRUE_GUILDS
: ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_TRUE_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف حقیقی"
/>
</NavLink>
<NavLink
to={
getRoleFromUrl() === "SuperAdmin"
? ROUTE_SUPER_ADMIN_ROUTE_OUT_PROVINCE_LEGAL_GUILDS
: getRoleFromUrl() === "AdminX"
? ROUTE_ADMINX_ROUTE_OUT_PROVINCE_LEGAL_GUILDS
: ROUTE_PROVINCE_ROUTE_OUT_PROVINCE_LEGAL_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف حقوقی"
/>
</NavLink>
</>
)}
</Grid>
</>
);
};

View File

@@ -0,0 +1,62 @@
import React, { useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { useDispatch, useSelector } from "react-redux";
import { getGuildsSettingsService } from "../../services/get-guilds-settings";
import { ManageGuildsState } from "../manage-guilds-state/ManageGuildsState";
import { ManageStewardsState } from "../manage-stewards-state/ManageStewardsState";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const GuildsSettings = () => {
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState([]);
const { getGuildsSettings } = useSelector((state) => state.provinceSlice);
useEffect(() => {
dispatch(LOADING_START());
dispatch(getGuildsSettingsService({ kill_house_key: null })).then(() => {
dispatch(LOADING_END());
});
}, []);
useEffect(() => {
if (Array.isArray(getGuildsSettings)) {
const d = getGuildsSettings?.map((item, i) => {
return [
i + 1,
item.killHouse.name,
<ManageGuildsState
key={i}
guildState={item.guilds}
choose_steward_guilds_key={item.key}
/>,
<ManageStewardsState
key={i}
stewardState={item.steward}
choose_steward_guilds_key={item.key}
/>,
];
});
setDataTable(d);
}
}, [getGuildsSettings]);
return (
<Grid>
<ResponsiveTable
paginated
title="مجوزهای توزیع"
columns={[
"ردیف",
"نام کشتارگاه",
"اجازه فروش آزاد به صنف",
"اجازه فروش آزاد به مباشرین",
]}
data={dataTable}
/>
</Grid>
);
};

View File

@@ -0,0 +1,60 @@
import { Button, TextField, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
export const KillProcessStats = ({ province }) => {
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const [selectedDate2, setSelectedDate2] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
useEffect(() => {
var currentDate = moment(selectedDate1);
var newDate = currentDate.add(7, "days").format("YYYY-MM-DD");
setSelectedDate2(newDate);
}, [selectedDate1]);
return (
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid container alignItems="center" gap={SPACING.TINY}>
<Typography>روند کشتار بر اساس بازه</Typography>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>{" "}
<DatePicker
label="تا تاریخ"
id="date"
disabled={true}
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<a
href={`${province}killing_process_from_date_excel/?start=${selectedDate1}&end=${selectedDate2}`}
rel="noreferrer"
>
<Button variant="outlined" size="large">
دانلود
</Button>
</a>
</Grid>
);
};

View File

@@ -0,0 +1,73 @@
import { Switch } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceGetCars } from "../../services/province-get-cars";
import { updateCarStateService } from "../../services/update-car-state";
export const ManageCarState = ({ item }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const [checked, setChecked] = useState(
getRoleFromUrl() === "KillHouse"
? item.addCarActiveState
: item.driverActiveState
);
const handleChange = (event) => {
let reqObj;
if (getRoleFromUrl() === "KillHouse") {
reqObj = {
add_car_key: item.addCarKey,
add_car_change_activation: event.target.checked,
};
} else {
reqObj = {
driver_key: item.key,
change_activation: event.target.checked,
};
}
dispatch(updateCarStateService(reqObj)).then((r) => {
if (r?.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r?.payload?.error,
severity: "error",
});
} else {
dispatch(provinceGetCars());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
setChecked(event.target.checked);
};
let disabled = false;
if (item.driverType === "rental") {
disabled = true;
}
return (
<>
<Switch
checked={checked}
onChange={handleChange}
name="mySwitch"
color="primary"
disabled={disabled}
/>
{checked ? (
<label htmlFor="mySwitch">فعال</label>
) : (
<label htmlFor="mySwitch">غیرفعال</label>
)}
</>
);
};

View File

@@ -0,0 +1,184 @@
import {
Grid,
Box,
Button,
Checkbox,
CircularProgress,
FormControlLabel,
FormGroup,
Stack,
} from "@mui/material";
import axios from "axios";
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { AppContext } from "../../../../contexts/AppContext";
const LOSS_FIELDS = [
{
key: "includeTotalDiseaseLosses",
apiKey: "include_total_disease_losses",
label: "مجموع تلفات ناشی از بیماری",
},
{
key: "includeTotalFlockDestruction",
apiKey: "include_total_flock_destruction",
label: "مجموع معدوم‌سازی گله",
},
{
key: "includeTotalNormalFlockLosses",
apiKey: "include_total_normal_flock_losses",
label: "مجموع تلفات عادی گله",
},
{
key: "includeTotalForceMajeureLosses",
apiKey: "include_total_force_majeure_losses",
label: "مجموع تلفات ناشی از عوامل قهری و طبیعی",
},
{
key: "includeTotalFireLosses",
apiKey: "include_total_fire_losses",
label: "مجموع تلفات ناشی از آتش‌سوزی",
},
];
const DEFAULT_FORM_STATE = LOSS_FIELDS.reduce(
(acc, { key }) => ({ ...acc, [key]: false }),
{}
);
export const ManageEvacuations = () => {
const [openNotif] = useContext(AppContext);
const notify = useCallback(
({ severity, msg, vertical = "top", horizontal = "center" }) =>
openNotif({
severity,
msg,
vertical,
horizontal,
}),
[openNotif]
);
const [formState, setFormState] = useState(DEFAULT_FORM_STATE);
const [recordId, setRecordId] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
useEffect(() => {
const fetchLossManagement = async () => {
setIsLoading(true);
try {
const { data } = await axios.get("hatching-loss-management/");
setRecordId(data?.id ?? null);
setFormState((prev) =>
LOSS_FIELDS.reduce(
(acc, { key }) => ({
...acc,
[key]: Boolean(data?.[key]),
}),
{ ...prev }
)
);
} catch (error) {
const errorText =
error?.response?.data?.detail ??
error?.response?.data?.message ??
error?.message ??
"خطایی در دریافت اطلاعات رخ داده است.";
notify({ severity: "error", msg: errorText });
} finally {
setIsLoading(false);
}
};
fetchLossManagement();
}, []);
const handleChange = (fieldKey) => (event) => {
const { checked } = event.target;
setFormState((prev) => ({
...prev,
[fieldKey]: checked,
}));
};
const requestBody = useMemo(
() =>
LOSS_FIELDS.reduce((acc, { key, apiKey }) => {
acc[apiKey] = Boolean(formState[key]);
return acc;
}, {}),
[formState]
);
const handleSubmit = async (event) => {
event.preventDefault();
setIsSaving(true);
const targetId = recordId ?? 1;
try {
await axios.patch(`hatching-loss-management/${targetId}/`, requestBody);
notify({
severity: "success",
msg: "اطلاعات با موفقیت به‌روزرسانی شد.",
});
} catch (error) {
const errorText =
error?.response?.data?.detail ??
error?.response?.data?.message ??
error?.message ??
"خطایی در به‌روزرسانی اطلاعات رخ داده است.";
notify({ severity: "error", msg: errorText });
} finally {
setIsSaving(false);
}
};
return (
<Grid
component="section"
xs={12}
style={{ padding: "20px", width: "100%" }}
>
{isLoading ? (
<Stack direction="row" justifyContent="center">
<CircularProgress />
</Stack>
) : (
<Grid component="form" onSubmit={handleSubmit} xs={12}>
<FormGroup>
{LOSS_FIELDS.map(({ key, label }) => (
<FormControlLabel
key={key}
control={
<Checkbox
checked={Boolean(formState[key])}
onChange={handleChange(key)}
name={key}
disabled={isSaving}
/>
}
label={label}
/>
))}
</FormGroup>
<Box>
<Button
type="submit"
variant="contained"
disabled={isSaving}
color="primary"
>
{isSaving ? "در حال ذخیره..." : "به‌روزرسانی"}
</Button>
</Box>
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,576 @@
import React, { useContext, useEffect, useState } from "react";
import { Button, Checkbox, IconButton, TextField } from "@mui/material";
import axios from "axios";
import { RiSearchLine } from "react-icons/ri";
import { useDispatch } from "react-redux";
import EditIcon from "@mui/icons-material/Edit";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ProvinceManageDistributionsSubmitCommitmentPercent } from "../province-manage-distributions-submit-commitment-percentet/ProvinceManageDistributionsSubmitCommitmentPercent";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { ProvinceManageGuildDistributionsSubmitPercents } from "../province-manage-guild-distributions-submit-percents/ProvinceManageGuildDistributionsSubmitPercents";
import {
provinceManageGuildTradesEditAllPercents,
provinceManageGuildTradesEditPercents,
provinceManageGuildTradesGetPercents,
} from "../../services/province-manage-guilds-trades-edit-percents";
export const ManageGuildDistributions = () => {
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
const getDashboardData = () => {
dispatch(provinceManageGuildTradesGetPercents()).then((r) => {
setDashboardData(r.payload.data);
});
};
const fetchApiData = async (page) => {
let response;
dispatch(LOADING_START());
response = await axios.get(
`guilds-for-configs/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}`
);
getDashboardData();
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
const openGovModal = (item, isAll) => {
dispatch(
OPEN_MODAL({
title: "فروش دولتی",
content: (
<ProvinceManageGuildDistributionsSubmitPercents
item={item}
updateTable={updateTable}
type="gov"
isDashboard={isAll}
/>
),
})
);
};
const openFreeModal = (item, isAll) => {
dispatch(
OPEN_MODAL({
title: "فروش آزاد",
content: (
<ProvinceManageGuildDistributionsSubmitPercents
item={item}
updateTable={updateTable}
type="free"
isDashboard={isAll}
/>
),
})
);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
`${item?.user?.fullname} (${item?.user?.mobile})`,
<ProvinceManageDistributionsSubmitCommitmentPercent
key={i}
amount={item?.outProvinceFreeBuyingCommitmentPercent}
updateTable={updateTable}
item={item}
isGuild
/>,
<Checkbox
key={i}
checked={item?.freeSaleFromFreeQuotaInProvince}
onClick={() => {
dispatch(
provinceManageGuildTradesEditPercents({
key: item?.key,
free_sale_from_free_quota_in_province:
!item?.freeSaleFromFreeQuotaInProvince,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
/>,
<Checkbox
key={i}
checked={item?.freeSaleFormGovernmentalQuota}
onClick={() => {
dispatch(
provinceManageGuildTradesEditPercents({
key: item?.key,
free_sale_form_governmental_quota:
!item?.freeSaleFormGovernmentalQuota,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
/>,
<Grid
key={i}
container
alignItems="center"
xs={12}
justifyContent="start"
direction="row"
>
<Grid xs={6}>
<Checkbox
checked={item?.governmentalSellingPermission}
onChange={() => {
const newValue = !item?.governmentalSellingPermission;
dispatch(
provinceManageGuildTradesEditPercents({
key: item?.key,
governmental_selling_permission: newValue,
...(newValue === false
? {
in_province_governmental_selling_percent: 0,
segmentation_governmental_percent: 0,
out_province_governmental_selling_percent: 0,
}
: {}),
})
).then(() => {
if (newValue === true) {
openGovModal(item);
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
updateTable();
});
}}
/>
</Grid>
{item?.governmentalSellingPermission && (
<Grid xs={6}>
<IconButton
size="small"
onClick={() => openGovModal(item)}
color="primary"
>
<EditIcon />
</IconButton>
</Grid>
)}
</Grid>,
<Grid
key={i}
container
alignItems="center"
xs={12}
justifyContent="center"
direction="row"
>
<Grid xs={6}>
<Checkbox
checked={item?.freeSellingPermission}
onChange={() => {
const newValue = !item?.freeSellingPermission;
dispatch(
provinceManageGuildTradesEditPercents({
key: item?.key,
free_selling_permission: newValue,
...(newValue === false
? {
in_province_free_selling_percent: 0,
out_province_free_selling_percent: 0,
segmentation_free_selling_percent: 0,
}
: {}),
})
).then(() => {
if (newValue === true) {
openFreeModal(item);
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
updateTable();
});
}}
/>
</Grid>
<Grid xs={6}>
{item?.freeSellingPermission && (
<IconButton
size="small"
onClick={() => openFreeModal(item)}
color="primary"
>
<EditIcon />
</IconButton>
)}{" "}
</Grid>
</Grid>,
item?.inProvinceGovernmentalSellingPercent,
item?.outProvinceGovernmentalSellingPercent,
item?.segmentationGovernmentalPercent,
item?.inProvinceFreeSellingPercent,
item?.outProvinceFreeSellingPercent,
item?.segmentationFreeSellingPercent,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`guilds-for-configs/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"درصد تحویل خرید خارج استان",
"فروش آزاد به داخل استان",
"فروش آزاد از سهمیه دولتی",
"فروش دولتی",
"فروش آزاد",
"درصد فروش دولتی داخل استان",
"درصد فروش دولتی خارج استان",
"درصد فروش دولتی قطعه بندی",
"درصد فروش آزاد داخل استان",
"درصد فروش آزاد خارج استان",
"درصد فروش آزاد قطعه بندی",
]}
data={[
[
<ProvinceManageDistributionsSubmitCommitmentPercent
key={dashboardData}
amount={dashboardData?.outProvinceFreeBuyingCommitmentPercent}
updateTable={updateTable}
item={dashboardData}
isGuild
isDashboard
/>,
<Checkbox
key={dashboardData}
checked={dashboardData?.freeSaleFromFreeQuotaInProvince}
onClick={() => {
dispatch(
provinceManageGuildTradesEditAllPercents({
free_sale_from_free_quota_in_province:
!dashboardData?.freeSaleFromFreeQuotaInProvince,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
/>,
<Checkbox
key={dashboardData}
checked={dashboardData?.freeSaleFormGovernmentalQuota}
onClick={() => {
dispatch(
provinceManageGuildTradesEditAllPercents({
free_sale_form_governmental_quota:
!dashboardData?.freeSaleFormGovernmentalQuota,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
/>,
<Grid
key={dashboardData}
container
alignItems="center"
xs={12}
justifyContent="start"
direction="row"
>
<Grid xs={6}>
<Checkbox
checked={dashboardData?.governmentalSellingPermission}
onChange={() => {
const newValue =
!dashboardData?.governmentalSellingPermission;
dispatch(
provinceManageGuildTradesEditAllPercents({
governmental_selling_permission: newValue,
...(newValue === false
? {
in_province_governmental_selling_percent: 0,
segmentation_governmental_percent: 0,
out_province_governmental_selling_percent: 0,
}
: {}),
})
).then(() => {
if (newValue === true) {
openGovModal(dashboardData, true);
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
updateTable();
});
}}
/>
</Grid>
{dashboardData?.governmentalSellingPermission && (
<Grid xs={6}>
<IconButton
size="small"
onClick={() => openGovModal(dashboardData, true)}
color="primary"
>
<EditIcon />
</IconButton>
</Grid>
)}
</Grid>,
<Grid
key={dashboardData}
container
alignItems="center"
xs={12}
justifyContent="center"
direction="row"
>
<Grid xs={6}>
<Checkbox
checked={dashboardData?.freeSellingPermission}
onChange={() => {
const newValue = !dashboardData?.freeSellingPermission;
dispatch(
provinceManageGuildTradesEditAllPercents({
free_selling_permission: newValue,
...(newValue === false
? {
in_province_free_selling_percent: 0,
out_province_free_selling_percent: 0,
segmentation_free_selling_percent: 0,
}
: {}),
})
).then(() => {
if (newValue === true) {
openFreeModal(dashboardData, true);
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
updateTable();
});
}}
/>
</Grid>
<Grid xs={6}>
{dashboardData?.freeSellingPermission && (
<IconButton
size="small"
onClick={() => openFreeModal(dashboardData, true)}
color="primary"
>
<EditIcon />
</IconButton>
)}{" "}
</Grid>
</Grid>,
dashboardData?.inProvinceGovernmentalSellingPercent,
dashboardData?.outProvinceGovernmentalSellingPercent,
dashboardData?.segmentationGovernmentalPercent,
dashboardData?.inProvinceFreeSellingPercent,
dashboardData?.outProvinceFreeSellingPercent,
dashboardData?.segmentationFreeSellingPercent,
],
]}
title={"تنظیمات کلی"}
/>
</Grid>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"نام مباشر",
"درصد تحویل خرید خارج استان",
"فروش آزاد به داخل استان",
"فروش آزاد از سهمیه دولتی",
"فروش دولتی",
"فروش آزاد",
"درصد فروش دولتی داخل استان",
"درصد فروش دولتی خارج استان",
"درصد فروش دولتی قطعه بندی",
"درصد فروش آزاد داخل استان",
"درصد فروش آزاد خارج استان",
"درصد فروش آزاد قطعه بندی",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مباشرین"
/>
</Grid>
);
};

View File

@@ -0,0 +1,285 @@
import {
Button,
FormControlLabel,
IconButton,
Popover,
Switch,
Tooltip,
} from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import axios from "axios";
import { Grid } from "../../../../components/grid/Grid";
import {
DRAWER,
OPEN_MODAL,
LOADING_START,
LOADING_END,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { CreateGuilds } from "../create-guilds/CreateGuilds";
import { GuildLimitaion } from "../guild-limitaion/GuildLimitaion";
import ManageHistoryIcon from "@mui/icons-material/ManageHistory";
import VisibilityIcon from "@mui/icons-material/Visibility";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import SendIcon from "@mui/icons-material/Send";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { ProvinceManageGuildsSubmitRegisterCode } from "../province-manage-guilds-submit-register-code/ProvinceManageGuildsSubmitRegisterCode";
import { provinceResendRegisterCodeStateService } from "../../services/province-get-register-code-state";
import { ViewGuildDetails } from "../view-guild-details/ViewGuildDetails";
import TuneIcon from "@mui/icons-material/Tune";
export const ManageGuildsOperations = ({ guild, updateTable }) => {
const dispatch = useDispatch();
const [checked, setChecked] = useState(guild.active);
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const [openNotif] = useContext(AppContext);
const openPopover = (event) => {
setPopoverOpen(true);
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setPopoverOpen(false);
setAnchorEl(null);
};
const handleChange = async (event) => {
const newChecked = event.target.checked;
setChecked(newChecked);
dispatch(LOADING_START());
try {
const response = await axios.put("guilds/0/", {
guilds_key: guild?.key,
active: newChecked,
role: getRoleFromUrl(),
});
dispatch(LOADING_END());
if (response.status === 200) {
updateTable();
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
} catch (error) {
dispatch(LOADING_END());
const errorMessage =
error.response?.data?.result ||
error.response?.data?.error ||
"خطا در انجام عملیات";
openNotif({
vertical: "top",
horizontal: "center",
msg: errorMessage,
severity: "error",
});
// Revert the checkbox state on error
setChecked(!newChecked);
}
};
return (
<Grid>
<Tooltip title="مدیریت صنف" placement="left">
<IconButton color="primary" onClick={openPopover}>
<TuneIcon />
</IconButton>
</Tooltip>
<Popover
open={popoverOpen}
anchorEl={anchorEl}
onClose={closePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<div style={{ padding: 10 }}>
<Grid
container
direction="column"
alignItems="flex-start"
justifyContent="center"
gap={1}
>
<Button
size="small"
color="info"
startIcon={<VisibilityIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "مشاهده جزییات صنف",
content: <ViewGuildDetails guild={guild} />,
size: window.innerWidth <= 600 ? "small" : "auto",
})
);
}}
>
جزییات
</Button>
{["AdminX", "SuperAdmin", "GuildRoom"].includes(
getRoleFromUrl()
) && (
<>
<FormControlLabel
control={
<Switch
checked={checked}
onChange={handleChange}
color="primary"
/>
}
label={checked ? "فعال" : "غیرفعال"}
style={{
justifyContent: "center",
alignItems: "center",
display: "flex",
}}
/>
<Button
color="primary"
size="small"
startIcon={<EditIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
// disabled={guild?.provinceAcceptState !== "pending"}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش واحد",
size: window.innerWidth <= 600 ? "small" : "auto",
content: (
<CreateGuilds
guild={guild}
updateTable={updateTable}
/>
),
})
);
}}
>
ویرایش
</Button>
<Button
size="small"
color="warning"
startIcon={<ManageHistoryIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "اعمال محدودیت",
content: (
<GuildLimitaion
guild={guild}
updateTable={updateTable}
/>
),
})
);
}}
>
محدودیت
</Button>
</>
)}
{!guild?.isRegistered && (
<Grid
container
alignItems="flex-start"
justifyContent="flex-start"
gap={SPACING.TINY}
direction="column"
>
<Button
size="small"
color="success"
startIcon={<CheckCircleIcon size={30} />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "ثبت کد احراز",
content: (
<ProvinceManageGuildsSubmitRegisterCode
item={guild}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت کد احراز
</Button>
<Button
size="small"
color="secondary"
startIcon={<SendIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
provinceResendRegisterCodeStateService({
key: guild.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "کد با موفقیت ارسال شد.",
severity: "success",
});
}
});
}}
>
ارسال مجدد کد
</Button>
</Grid>
)}
</Grid>
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,314 @@
import { Button, IconButton, Popover, TextField } from "@mui/material";
import { useContext, useState } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import VisibilityIcon from "@mui/icons-material/Visibility";
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import SendIcon from "@mui/icons-material/Send";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { checkKillhouseRequestGuildService } from "../../services/check-killhouse-request-guild";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { ProvinceManageGuildsSubmitRegisterCode } from "../province-manage-guilds-submit-register-code/ProvinceManageGuildsSubmitRegisterCode";
import { provinceResendRegisterCodeStateService } from "../../services/province-get-register-code-state";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ViewGuildDetails } from "../view-guild-details/ViewGuildDetails";
export const ManageGuildsRequestsOperations = ({ guild, updateTable }) => {
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const openPopover = (event) => {
setPopoverOpen(true);
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setPopoverOpen(false);
setAnchorEl(null);
};
return (
<Grid>
<IconButton variant="contained" color="primary" onClick={openPopover}>
<TuneIcon />
</IconButton>
<Popover
open={popoverOpen}
anchorEl={anchorEl}
onClose={closePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<div style={{ padding: 10 }}>
<Grid
container
direction="column"
alignItems="flex-start"
justifyContent="center"
gap={1}
>
{/* {!readOnly && (
<Tooltip placement="left" title="بازگشت جوجه ریزی">
<IconButton
variant="outlined"
onClick={() => {
dispatch(
hatchingUndoArchiveService({
key: item.key,
type: "return_archive",
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateArchive(1);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<KeyboardReturnIcon />
</IconButton>
</Tooltip>
)} */}
{["SuperAdmin", "AdminX", "GuildRoom"].includes(
getRoleFromUrl()
) && (
<Button
size="small"
color="info"
startIcon={<VisibilityIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "مشاهده جزییات صنف",
content: <ViewGuildDetails guild={guild} />,
size: window.innerWidth <= 600 ? "small" : "auto",
})
);
}}
>
جزییات
</Button>
)}
{(guild?.activeRegisterCode && guild?.loggedRegisterCode) ||
!guild?.activeRegisterCode ? (
<Grid
container
alignItems="flex-start"
justifyContent="flex-start"
gap={SPACING.TINY}
direction="column"
>
<Button
size="small"
color="success"
startIcon={<CheckIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
checkKillhouseRequestGuildService({
guilds_key: guild.key,
state: "accepted",
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
size="small"
color="error"
startIcon={<CloseIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "رد درخواست ثبت صنف",
content: (
<RejectModal
guild={guild}
updateTable={updateTable}
/>
),
})
);
}}
>
رد
</Button>
</Grid>
) : (
<Grid
container
alignItems="flex-start"
justifyContent="flex-start"
gap={SPACING.TINY}
direction="column"
>
<Button
size="small"
color="success"
startIcon={<CheckCircleIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "ثبت کد احراز",
content: (
<ProvinceManageGuildsSubmitRegisterCode
item={guild}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت کد احراز
</Button>
<Button
size="small"
color="secondary"
startIcon={<SendIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
provinceResendRegisterCodeStateService({
key: guild.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "کد با موفقیت ارسال شد.",
severity: "success",
});
}
});
}}
>
ارسال مجدد کد
</Button>
</Grid>
)}
</Grid>
</div>
</Popover>
</Grid>
);
};
const RejectModal = ({ guild, updateTable }) => {
const [openNotif] = useContext(AppContext);
const [text, setText] = useState("");
const dispatch = useDispatch();
return (
<Grid container gap={SPACING.TINY}>
<TextField
placeholder="دلیل رد درخواست..."
value={text}
onChange={(e) => setText(e.target.value)}
/>
<Button
fullWidth
variant="contained"
color="primary"
disabled={!text}
onClick={() => {
dispatch(
checkKillhouseRequestGuildService({
guilds_key: guild.key,
state: "rejected",
message: text,
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,191 @@
import React, { useEffect, useState } from "react";
import { Button, TextField } from "@mui/material";
import { useDispatch } from "react-redux";
import axios from "axios";
import { RiSearchLine } from "react-icons/ri";
import {
// DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { ManageGuildsRequestsOperations } from "../manage-guilds-requests-operations/ManageGuildsRequestsOperations";
import { CreateGuilds } from "../create-guilds/CreateGuilds";
export const ManageGuildsRequests = () => {
const dispatch = useDispatch();
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`total_guilds/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&check=true&state=pending`
);
dispatch(LOADING_END());
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
// item?.killHouseInfo?.length
// ? item?.killHouseInfo
// ?.map((item) => {
// const type = item?.killer ? "کشتارکن" : "کشتارگاه";
// return `${type} ${item?.name} (${item?.mobile})`;
// })
// .join(" - ")
// :
`${item?.registerarFullname || ""} ${
item?.registerarMobile ? "(" + item?.registerarMobile + " )" : " "
}`,
item?.licenseNumber || "-",
item?.guildsName || "-",
`${item?.user?.fullname || "-"} (${item?.user?.mobile || "-"})`,
item?.user?.nationalId || "-",
item?.typeActivity || "-",
item?.areaActivity || "-",
item?.address?.postalCode || "-",
`${item?.address?.province?.name || "-"}/${
item?.address?.city?.name || "-"
}/${item?.address?.address || "-"}`,
item?.steward ? "می باشد" : "نمی باشد",
item?.hasInquiry ? "استعلامی" : "دستی",
item?.activeRegisterCode ? "انجام شده" : "انجام نشده",
<ManageGuildsRequestsOperations
updateTable={updateTable}
key={item?.guildsName}
guild={item}
/>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`total_guilds/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}&check=true&state=pending`
);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
dispatch(LOADING_END());
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
{["AdminX", "SuperAdmin"].includes(getRoleFromUrl()) && (
<Button
variant="contained"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت واحد جدید",
size: window.innerWidth <= 600 ? "small" : "auto",
content: <CreateGuilds updateTable={updateTable} />,
})
);
}}
>
ثبت واحد جدید
</Button>
)}
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"ثبت کننده",
"شناسه صنف",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"نوع فعالیت",
"حوزه فعالیت",
"کدپستی",
"استان/شهر/آدرس",
"مباشر",
"نوع ثبت",
"احراز شماره موبایل",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="درخواست های ثبت صنف"
/>
</Grid>
);
};

View File

@@ -0,0 +1,41 @@
import { FormControlLabel, Switch } from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { updateGuildsSettingsService } from "../../services/update-guilds-settings";
export const ManageGuildsState = ({
guildState,
choose_steward_guilds_key,
}) => {
const dispatch = useDispatch();
const [switchState, setSwitchState] = useState(guildState);
const handleSwitchChange = () => {
setSwitchState((prevState) => {
dispatch(
updateGuildsSettingsService({
choose_steward_guilds_key,
guilds: !prevState,
})
);
return !prevState;
});
};
return (
<Grid container alignItems="center" justifyContent="center">
<FormControlLabel
control={
<Switch
checked={switchState}
onChange={handleSwitchChange}
name="switchState"
color="primary"
/>
}
// label="Toggle Switch"
/>
</Grid>
);
};

View File

@@ -0,0 +1,417 @@
import React, { useEffect, useState } from "react";
import { Button, TextField, Tooltip, MenuItem } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceGetTotalGuildsService } from "../../services/province-get-total-guilds";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { ManageGuildsOperations } from "../manage-guilds-operations/ManageGuildsOperations";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { SPACING } from "../../../../data/spacing";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { CreateGuilds } from "../create-guilds/CreateGuilds";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const MaangeGuilds = () => {
const userKey = useSelector((state) => state.userSlice?.userProfile?.key);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [activeState, setActiveState] = useState("active");
const handleTextChange = (e) => setTextValue(e.target.value);
// const handleCheckboxChange = () => setIsSteward(!isSteward);
const fetchApiData = async (page) => {
const response = await dispatch(
provinceGetTotalGuildsService({
search: "filter",
value: textValue,
page: page,
page_size: perPage,
steward: false,
active_state: activeState,
is_real_person: true,
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
setTotalRows(response.payload.data?.count || 0);
}
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
fetchApiData(1);
}, [perPage, selectedSubUser?.key, activeState]);
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const commonData = [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.licenseNumber || "-",
item?.guildsName || "-",
`${item?.user?.fullname || "-"} (${item?.user?.mobile || "-"})`,
item?.user?.nationalId ? item?.user?.nationalId : "-",
item?.typeActivity || "-",
item?.areaActivity || "-",
item?.address?.postalCode || "-",
`${item?.address?.province?.name || "-"}/${
item?.address?.city?.name || "-"
}/${item?.address?.address || "-"}`,
item?.steward ? "می باشد" : "نمی باشد",
item?.limitationAllocation ? "دارد" : "ندارد",
item?.allocationLimit ? item?.allocationLimit : "-",
item?.getPosStatus?.hasActivePons ? "دارد" : "ندارد",
item?.getPosStatus?.lenActiveSessions || "-",
];
if (getRoleFromUrl() !== "KillHouse") {
commonData.push(
<Button
key={`stewards-${i}`}
onClick={() => {
const tableData = item?.stewards?.map((option, index) => [
index + 1,
option?.guildsName || "-",
option?.user?.mobile || "-",
]);
dispatch(
OPEN_MODAL({
title: "مباشرین",
content: (
<SimpleTable
paginated
title={"مباشرین"}
columns={["ردیف", "نام و نام خانوادگی", "تلفن"]}
data={tableData}
/>
),
})
);
}}
>
نمایش ({item?.stewards?.length || 0})
</Button>
);
commonData.push(
<Button
key={`killHouse-${i}`}
onClick={() => {
const tableData = item?.killHouse?.map((option, index) => [
index + 1,
option?.name || "-",
option?.killHouseOperator?.user?.mobile || "-",
]);
dispatch(
OPEN_MODAL({
title: "کشتارگاه ها",
content: (
<SimpleTable
paginated
title={"کشتارگاه ها"}
columns={["ردیف", "نام و نام خانوادگی", "تلفن"]}
data={tableData}
/>
),
})
);
}}
>
نمایش ({item?.killHouse?.length || 0})
</Button>
);
}
if (
!["CityJahad", "CityPoultry", "KillHouse"].includes(getRoleFromUrl())
) {
commonData.splice(
14,
0,
item?.productInfo?.totalCarcassesWeight
? item.productInfo.totalCarcassesWeight.toLocaleString()
: "-"
);
commonData.splice(10, 0, item?.hasInquiry ? "استعلامی" : "دستی");
commonData.splice(
11,
0,
item?.activeRegisterCode ? "انجام شده" : "انجام نشده"
);
}
commonData?.push(item?.active ? "فعال" : "غیر فعال");
commonData.push(
!item?.isRegistered &&
item?.registerCode &&
!item?.loggedRegistrationCode
? "در انتظار ورود کد احراز"
: item?.isRegistered && item?.provinceAcceptState === "pending"
? "در انتظار تایید استان"
: item?.isRegistered
? "احراز شده"
: "احراز نشده"
);
if (getRoleFromUrl() !== "KillHouse") {
commonData.push(
<ManageGuildsOperations
guild={item}
updateTable={updateTable}
isModal={true}
/>
);
}
return commonData;
});
setTableData(d);
}, [data]);
const handleSubmit = async (e) => {
e.preventDefault();
const response = await dispatch(
provinceGetTotalGuildsService({
search: "filter",
value: textValue,
page: page,
page_size: perPage,
steward: false,
active_state: activeState,
is_real_person: true,
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
setTotalRows(response.payload.data?.count || 0);
}
};
return (
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={2}
mt={2}
>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
{["KillHouse", "GuildRoom"].includes(getRoleFromUrl()) && (
<Button
variant="contained"
onClick={() => {
dispatch(
OPEN_MODAL({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
size: window.innerWidth <= 600 ? "small" : "auto",
title: "ثبت واحد جدید",
content: <CreateGuilds updateTable={updateTable} />,
})
);
}}
>
ثبت واحد جدید
</Button>
)}
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
onChange={handleTextChange}
/>
{/* <FormControlLabel
style={{ marginRight: 4 }}
control={
<Checkbox
checked={isSteward}
onChange={handleCheckboxChange}
name="checkboxName"
color="primary"
/>
}
label="نمایش مباشرین"
/> */}
{getRoleFromUrl() !== "KillHouse" && (
<TextField
select
size="small"
label="وضعیت"
value={activeState}
onChange={(e) => setActiveState(e.target.value)}
sx={{ width: 150 }}
>
<MenuItem value="all">همه</MenuItem>
<MenuItem value="active">فعال</MenuItem>
<MenuItem value="deactive">غیرفعال</MenuItem>
</TextField>
)}
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
{getRoleFromUrl() !== "KillHouse" && (
<Grid>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}guilds_excel/?key=${userKey}&search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("province")
? `&role_key=${selectedSubUser?.key}`
: ""
}&active_state=${activeState}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
)}
</Grid>
<ResponsiveTable
data={tableData}
columns={
getRoleFromUrl() === "CityJahad" || getRoleFromUrl() === "CityPoultry"
? [
"ردیف",
"شناسه صنف",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"نوع فعالیت",
"حوزه فعالیت",
"کدپستی",
"استان/شهر/آدرس",
"مباشر",
"محدودیت تخصیص",
"حداکثر تخصیص",
"وضعیت کارتخوان",
"تعداد کارتخوان ",
"مباشرین",
"وضعیت فعالیت",
"وضعیت",
"کشتارگاه ها",
]
: getRoleFromUrl() === "KillHouse"
? [
"ردیف",
"شناسه صنف",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"نوع فعالیت",
"حوزه فعالیت",
"کدپستی",
"استان/شهر/آدرس",
"مباشر",
"محدودیت تخصیص",
"حداکثر تخصیص",
"وضعیت کارتخوان",
"تعداد کارتخوان ",
"وضعیت فعالیت",
"وضعیت",
// "عملیات",
]
: [
"ردیف",
"شناسه صنف",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"نوع فعالیت",
"حوزه فعالیت",
"کدپستی",
"استان/شهر/آدرس",
"مباشر",
"نوع ثبت",
"احراز شماره موبایل",
"محدودیت تخصیص",
"حداکثر تخصیص",
"وضعیت کارتخوان",
"تعداد کارتخوان ",
"وزن خرید (کیلوگرم)",
"مباشرین",
"کشتارگاه ها",
"وضعیت فعالیت",
"وضعیت",
"عملیات",
]
}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مدیریت اصناف"
/>
</Grid>
);
};

View File

@@ -0,0 +1,663 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
IconButton,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { Grid } from "../../../../components/grid/Grid";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { EditPoultryCity } from "../edit-poultry-city/EditPoultryCity";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { RiFileExcel2Fill } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { provinceGetDashboardPoultriesService } from "../../services/getDashboardOfManagePoultries";
import { getPoultryActiveLimitedService } from "../../services/province-get-poultry-active-limited";
import Chip from "@mui/material/Chip";
import TuneIcon from "@mui/icons-material/Tune";
import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
import FileDownloadIcon from "@mui/icons-material/FileDownload";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import ToggleOffIcon from "@mui/icons-material/ToggleOff";
import VisibilityIcon from "@mui/icons-material/Visibility";
export const ManagePoultries = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const userKey = useSelector((state) => state.userSlice?.userProfile?.key);
const [dashboardData, setDashboardData] = useState([]);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [has500Error, setHas500Error] = useState(false);
const PoultryActions = ({ poultryItem, onToggleLimit }) => {
const navigate = useNavigate();
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleEdit = () => {
dispatch(
OPEN_MODAL({
title: "ویرایش تعاونی",
content: (
<EditPoultryCity updateTable={updateTable} item={poultryItem} />
),
})
);
handleClose();
};
const handleDownload = () => {
if (poultryItem?.key) {
window.open(
`${axios.defaults.baseURL}poultry_monitoring_excel/?key=${poultryItem.key}`,
"_blank"
);
}
handleClose();
};
const handleViewDetails = () => {
if (poultryItem?.breedingUniqueId) {
navigate(
window.location.pathname +
`${poultryItem.breedingUniqueId}?from=Poultry`
);
}
handleClose();
};
return (
<Grid>
<Tooltip title={"تنظیمات"} placement="right-start">
<IconButton
color="primary"
className="avicultureActiveRequestsBtn"
onClick={handleOpen}
>
<TuneIcon />
</IconButton>
</Tooltip>
<Popover
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<List sx={{ minWidth: 220, p: 1 }}>
<ListItemButton
sx={{ py: 1 }}
onClick={() => onToggleLimit(!poultryItem?.orderLimit)}
>
<ListItemIcon>
{poultryItem?.orderLimit ? (
<ToggleOnIcon color="success" />
) : (
<ToggleOffIcon color="disabled" />
)}
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
محدودیت ثبت سفارش
</Typography>
}
/>
</ListItemButton>
<ListItemButton onClick={handleViewDetails} sx={{ py: 1 }}>
<ListItemIcon>
<VisibilityIcon color="primary" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
مشاهده جزییات
</Typography>
}
/>
</ListItemButton>
<ListItemButton
disabled={getRoleFromUrl() === "VetSupervisor"}
onClick={handleEdit}
sx={{ py: 1 }}
>
<ListItemIcon>
<ManageAccountsIcon color="secondary" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
ویرایش تعاونی
</Typography>
}
/>
</ListItemButton>
<ListItemButton onClick={handleDownload} sx={{ py: 1 }}>
<ListItemIcon>
<FileDownloadIcon color="success" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
خروجی اکسل
</Typography>
}
/>
</ListItemButton>
</List>
</Popover>
</Grid>
);
};
const fetchApiData = async (page) => {
// Prevent request if there was a 500 error
if (has500Error) {
return;
}
dispatch(LOADING_START());
try {
const response = await axios.get(
`total_poultry/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&search=filter&value=${textValue}`
);
// Reset error state on successful response
setHas500Error(false);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
} catch (error) {
console.error("Error fetching data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setData([]);
setTotalRows(0);
} finally {
dispatch(LOADING_END());
}
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
fetchApiData(1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
// Prevent request if there was a 500 error
if (has500Error) {
return;
}
dispatch(provinceGetDashboardPoultriesService({ textValue }))
.then((r) => {
if (r?.error) {
const errorMessage = r.error?.message || "";
const is500Error =
errorMessage.includes("500") ||
r.error?.status === 500 ||
r.error?.statusCode === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setDashboardData([]);
} else if (r?.payload?.data) {
// Reset error state on successful response
setHas500Error(false);
setDashboardData(r.payload.data);
} else {
setDashboardData([]);
}
})
.catch((error) => {
console.error("Error fetching dashboard data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setDashboardData([]);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Reset error state when textValue changes
useEffect(() => {
setHas500Error(false);
}, [textValue]);
const handleOrderLimitChange = (poultryKey, newChecked) => {
dispatch(
getPoultryActiveLimitedService({
key: poultryKey,
order_limit: newChecked,
})
)
.then((r) => {
if (r?.error) {
const errorMessage = r.error?.message || "";
const is500Error =
errorMessage.includes("500") ||
r.error?.status === 500 ||
r.error?.statusCode === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در تغییر وضعیت محدودیت پیش آمده است!",
severity: "error",
});
}
} else {
// Reset error state on successful response
setHas500Error(false);
updateTable();
}
})
.catch((error) => {
console.error("Error updating order limit:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در تغییر وضعیت محدودیت پیش آمده است!",
severity: "error",
});
}
});
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.unitName || "",
`${item?.user?.fullname || ""} (${item?.user?.mobile || ""})`,
item?.breedingUniqueId || "",
item?.epidemiologicalCode || "",
item?.healthCertificateNumber || "",
item?.numberOfHalls || 0,
item?.totalCapacity ? item.totalCapacity.toLocaleString() : "0",
`${item?.address?.province?.name || ""}/${
item?.address?.city?.name || ""
}`,
item?.cityOperator || "ندارد",
item?.vetFarm?.vetFarmName
? `${item.vetFarm.vetFarmName} (${item.vetFarm?.vetFarmMobile || ""})`
: "ندارد",
`${item?.hatchingInfo?.activeHatching ? "دارد" : "ندارد"} (${
item?.hatchingInfo?.period || "0"
})`,
<Chip
key={`orderLimitStatus-${i}`}
label={item?.orderLimit ? "فعال" : "غیرفعال"}
color={item?.orderLimit ? "success" : "default"}
variant={item?.orderLimit ? "filled" : "outlined"}
size="small"
sx={{
width: 80,
justifyContent: "center",
}}
/>,
<PoultryActions
key={`actions-${i}`}
poultryItem={item}
onToggleLimit={(checked) =>
handleOrderLimitChange(item?.key, checked)
}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
// Reset error state when perPage changes
setHas500Error(false);
fetchApiData(1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
// Reset error state on manual submit (user retry)
setHas500Error(false);
dispatch(LOADING_START());
try {
const response = await axios.get(
`total_poultry/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}&search=filter&value=${textValue}`
);
// Reset error state on successful response
setHas500Error(false);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
dispatch(LOADING_END());
dispatch(provinceGetDashboardPoultriesService({ textValue }))
.then((r) => {
if (r?.error) {
const errorMessage = r.error?.message || "";
const is500Error =
errorMessage.includes("500") ||
r.error?.status === 500 ||
r.error?.statusCode === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setDashboardData([]);
} else if (r?.payload?.data) {
setHas500Error(false);
setDashboardData(r.payload.data);
} else {
setDashboardData([]);
}
})
.catch((error) => {
console.error("Error fetching dashboard data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setDashboardData([]);
});
} catch (error) {
console.error("Error fetching data:", error);
const errorMessage = error?.message || "";
const status = error?.response?.status;
const is500Error = errorMessage.includes("500") || status === 500;
if (is500Error) {
setHas500Error(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی در دریافت اطلاعات پیش آمده است!",
severity: "error",
});
}
setData([]);
setTotalRows(0);
dispatch(LOADING_END());
}
};
return (
<Grid container xs={10} alignItems="center" justifyContent="center" mt={2}>
<Grid
container
alignItems="center"
gap={2}
paddingTop={2}
paddingRight={2}
mx={4}
mb={1}
>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
onClick={handleSubmit}
>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}management_poultry/?key=${
userKey || ""
}&role=${getRoleFromUrl()}&search=filter&value=${textValue || ""}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد مرغداران",
"تعداد کل دوره جوجه ریزی",
"حجم کل جوجه ریزی",
"جوجه ریزی های فعال",
" حجم جوجه ریزی فعال",
"حجم مانده در سالن فعال",
"تعداد کل بار",
"حجم کل بار",
"میانگین درصد افت",
]}
data={[
[
dashboardData?.poultryCounts
? dashboardData.poultryCounts.toLocaleString()
: "0",
dashboardData?.allPeriod
? dashboardData.allPeriod.toLocaleString()
: "0",
dashboardData?.allTotalQuantity
? dashboardData.allTotalQuantity.toLocaleString()
: "0",
dashboardData?.hatchingPending
? dashboardData.hatchingPending.toLocaleString()
: "0",
dashboardData?.totalQuantityPoultryHatchingPending
? dashboardData.totalQuantityPoultryHatchingPending.toLocaleString()
: "0",
dashboardData?.totalLeftOverPoultryHatchingPending
? dashboardData.totalLeftOverPoultryHatchingPending.toLocaleString()
: "0",
dashboardData?.allKillRequest
? dashboardData.allKillRequest.toLocaleString()
: "0",
dashboardData?.allKillRequestQuantity
? dashboardData.allKillRequestQuantity.toLocaleString()
: "0",
dashboardData?.percentLoss
? dashboardData.percentLoss.toLocaleString()
: "0",
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"نام واحد",
"مالک (تلفن)",
"شناسه یکتا",
"کداپیدمیولوژیک",
"کد بهداشتی",
"تعداد سالن",
"ظرفیت فارم (قطعه)",
"استان/شهر",
"تعاونی",
"دامپزشک فارم (تلفن)",
"جوجه ریزی فعال (تعداد دوره)",
"محدودیت ثبت سفارش",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مدیریت مرغداران"
/>
</Grid>
);
};

View File

@@ -0,0 +1,112 @@
import {
Button,
FormControlLabel,
IconButton,
Popover,
Switch,
Tooltip,
} from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { provinceChangeActiveStewardService } from "../../services/province-change-active-steward";
import { EditStewards } from "../edit-stewards/EditStewards";
import EditIcon from "@mui/icons-material/Edit";
import TuneIcon from "@mui/icons-material/Tune";
export const ManageStewardsOperations = ({ guild }) => {
const dispatch = useDispatch();
const [checked, setChecked] = useState(guild.active);
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const openPopover = (event) => {
setPopoverOpen(true);
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setPopoverOpen(false);
setAnchorEl(null);
};
const handleChange = (event) => {
setChecked(event.target.checked);
dispatch(
provinceChangeActiveStewardService({
steward_key: guild?.key,
active: event.target.checked,
})
);
};
return (
<Grid>
<Tooltip title="مدیریت متصدی" placement="left">
<IconButton color="primary" onClick={openPopover}>
<TuneIcon />
</IconButton>
</Tooltip>
<Popover
open={popoverOpen}
anchorEl={anchorEl}
onClose={closePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<div style={{ padding: 10 }}>
<Grid
container
direction="column"
alignItems="flex-start"
justifyContent="center"
gap={1}
>
<FormControlLabel
control={
<Switch
checked={checked}
onChange={handleChange}
color="primary"
/>
}
label={checked ? "فعال" : "غیرفعال"}
style={{
justifyContent: "center",
alignItems: "center",
display: "flex",
}}
/>
<Button
size="small"
color="primary"
startIcon={<EditIcon />}
sx={{ fontSize: { xs: 12, md: 15 } }}
onClick={() => {
closePopover();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "اعمال محدودیت",
content: <EditStewards guild={guild} />,
})
);
}}
>
اعمال محدودیت
</Button>
</Grid>
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,41 @@
import { FormControlLabel, Switch } from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { updateGuildsSettingsService } from "../../services/update-guilds-settings";
export const ManageStewardsState = ({
stewardState,
choose_steward_guilds_key,
}) => {
const dispatch = useDispatch();
const [switchState, setSwitchState] = useState(stewardState);
const handleSwitchChange = () => {
setSwitchState((prevState) => {
dispatch(
updateGuildsSettingsService({
choose_steward_guilds_key,
steward: !prevState,
})
);
return !prevState;
});
};
return (
<Grid container alignItems="center" justifyContent="center">
<FormControlLabel
control={
<Switch
checked={switchState}
onChange={handleSwitchChange}
name="switchState"
color="primary"
/>
}
// label="Toggle Switch"
/>
</Grid>
);
};

View File

@@ -0,0 +1,122 @@
import { Button, Tooltip, MenuItem, TextField } from "@mui/material";
import axios from "axios";
import { useEffect, useState } from "react";
import { RiFileExcel2Fill } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { provinceGetStewardsService } from "../../services/province-get-stewards";
import { ManageStewardsOperations } from "../manage-stewards-operations/ManageStewardsOperations";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const ManageStewards = () => {
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState([]);
const [statusFilter, setStatusFilter] = useState("همه");
const { provinceGetStewards } = useSelector((state) => state.provinceSlice);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
useEffect(() => {
dispatch(provinceGetStewardsService());
}, []);
useEffect(() => {
const filtered =
statusFilter === "همه"
? provinceGetStewards
: provinceGetStewards?.filter((item) =>
statusFilter === "فعال" ? item?.active : !item?.active
);
const d = filtered?.map((item, i) => {
const dataRow = [
i + 1,
item?.guilds?.guildsId,
item?.guilds?.guildsName,
`${item?.guilds?.user?.fullname} (${item?.guilds?.user?.mobile})`,
item?.guilds?.user?.nationalId,
item?.guilds?.typeActivity,
item?.guilds?.areaActivity,
item?.guilds?.licenseNumber,
`${item?.guilds?.address?.province.name}/${item?.guilds?.address?.city.name}`,
item?.guilds?.address?.address,
item?.limitationAllocation ? "دارد" : "ندارد",
item?.allocationLimit?.toLocaleString(),
item?.centersAllocation?.map((item) => item.label).join(" - "),
item?.productInfo?.totalCarcassesWeight?.toLocaleString(),
item?.productInfo?.realAllocatedWeight?.toLocaleString(),
<ManageStewardsOperations guild={item} key={i} />,
];
if (getRoleFromUrl() === "CityJahad") {
dataRow.pop();
}
return dataRow;
});
setDataTable(d);
}, [provinceGetStewards, statusFilter]);
const columns = [
"ردیف",
"شناسه صنف",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"نوع فعالیت",
"حوزه فعالیت",
"شماره مجوز",
"استان/شهرستان",
"آدرس واحد صنفی",
"محدودیت تخصیص",
"حداکثر تخصیص",
"مراکز تخصیص",
"وزن خرید (کیلوگرم)",
"وزن فروش (کیلوگرم)",
"عملیات",
];
if (getRoleFromUrl() === "CityJahad") {
columns.pop();
}
return (
<Grid container>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}stewards_excel/?role=${getRoleFromUrl()}&key=${userKey}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
<TextField
select
label="وضعیت"
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
size="small"
sx={{ width: 150 }}
>
<MenuItem value="همه">همه</MenuItem>
<MenuItem value="فعال">فعال</MenuItem>
<MenuItem value="غیرفعال">غیرفعال</MenuItem>
</TextField>
</Grid>
<ResponsiveTable
title="مباشرین"
paginated
columns={columns}
data={dataTable}
/>
</Grid>
);
};

View File

@@ -0,0 +1,36 @@
import { useState } from "react";
import { Tab, Tabs } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { MaangeGuilds } from "../manage-guilds/ManageGuilds";
import { PspActiveSession } from "../../../psp-company/components/psp-active-session/PspActiveSession";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const ManageTab = () => {
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Grid container alignItems="center" justifyContent="center">
<Grid xs={12} container alignItems="center" justifyContent="center">
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={value}
onChange={handleChange}
mt={2}
>
<Tab label="اصناف" value={0} />
{getRoleFromUrl() !== "KillHouse" && (
<Tab label="نشست ها" value={1} />
)}
</Tabs>
</Grid>
{value === 0 && <MaangeGuilds readOnly={true} />}
{value === 1 && <PspActiveSession readOnly={true} />}
</Grid>
);
};

View File

@@ -0,0 +1,88 @@
import { IconButton, Tooltip, Typography } from "@mui/material";
import VisibilityIcon from "@mui/icons-material/Visibility";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { slaughterGetKillerKillhousesService } from "../../../slaughter-house/services/slaughter-get-killers-killhouses";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export function MonitoringBuyers() {
const dispatch = useDispatch();
const navigate = useNavigate();
const { pathname } = useLocation();
const [dataTable, setDataTable] = useState();
const { slaughterGetKillerKillhouses } = useSelector(
(state) => state.slaughterSlice
);
// Get the first segment from pathname (adminx - could be superadmin or province)
const getPathPrefix = () => {
const segments = pathname.split("/").filter(Boolean);
return segments[0] || "";
};
useEffect(() => {
dispatch(slaughterGetKillerKillhousesService());
}, [dispatch]);
useEffect(() => {
const d = slaughterGetKillerKillhouses?.map((item, i) => {
const pathPrefix = getPathPrefix();
return [
i + 1,
item?.killer ? "کشتارکن" : "کشتارگاه",
<Grid key={item.key} alignItems="center" justifyContent="center">
<Typography variant="caption">{item?.name}</Typography>
</Grid>,
<Grid
container
key={item.key}
alignItems="center"
justifyContent="center"
>
<Typography variant="caption">
{item?.killHouseOperator?.user?.fullname} (
{item?.killHouseOperator?.user?.mobile})
</Typography>
</Grid>,
item?.uniqueIdentifier && (
<Tooltip title="مشاهده جزییات" key={item.key}>
<IconButton
size="small"
color="primary"
onClick={() => {
navigate(
`/${pathPrefix}/poultries/${item.uniqueIdentifier}?from=KillHouse`
);
}}
>
<VisibilityIcon fontSize="small" />
</IconButton>
</Tooltip>
),
];
});
setDataTable(d);
}, [slaughterGetKillerKillhouses, navigate, pathname]);
return (
<Grid mt={SPACING.SMALL}>
<ResponsiveTable
paginated
title="خریداران"
columns={[
"ردیف",
"ماهیت",
"نام واحد",
"نام و نام خانوادگی (تلفن)",
"مشاهده جزییات",
]}
data={dataTable}
/>
</Grid>
);
}

View File

@@ -0,0 +1,326 @@
import { Button } from "@mui/material";
import React, { useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ProvincePaymentPayDept } from "../province-payment-pay-dept/ProvincePaymentPayDept";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { useDispatch, useSelector } from "react-redux";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { parentCompanyGetWagesOfKillhousesService } from "../../services/province-get-wages-of-killhouses";
import { parentConpanyGetPaymentByWeightOverview } from "../../services/province-get-payment-by-weight-overview";
import { ProvinceWagePaymentTransactions } from "../province-wage-payment-transactions/ProvinceWagePaymentTransactions";
export const ParentCompanyPaymentByWeightOverviewTable = ({ province }) => {
const dispatch = useDispatch();
const [tableDataOfWages, setTableDataOfWages] = useState();
const { parentCompanyPaymentByWeightOverview } = useSelector(
(item) => item.provinceSlice
);
useEffect(() => {
dispatch(parentConpanyGetPaymentByWeightOverview(province));
if (getRoleFromUrl() !== "KillHouse") {
dispatch(parentCompanyGetWagesOfKillhousesService(province)).then((r) => {
const d = r.payload.data?.map((item, i) => {
return [
i + 1,
item?.name,
Math.ceil(item?.wageInfo?.totalWage)?.toLocaleString(),
Math.ceil(item?.wageInfo?.totalPaidWage)?.toLocaleString(),
Math.ceil(item?.wageInfo?.off)?.toLocaleString(),
Math.ceil(item?.wageInfo?.totalUnpaidWage)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.totalProvinceLiveWeight
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.totalProvinceCarcassesWeight
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.provinceKillRequestsTotalWeight
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.provinceKillRequestsTotalWage
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.freeBarsOutProvinceCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.freeBarsOutProvinceCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.freeBarsCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.freeBarsCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
item?.wageInfo?.freeBarsLiveTotalWeight
)?.toLocaleString(),
Math.ceil(item?.wageInfo?.freeBarsLiveTotalWage)?.toLocaleString(),
];
});
setTableDataOfWages(d);
});
}
}, [dispatch, province]);
return (
<Grid container justifyContent="center" xs={12} mt={1}>
<Grid isDashboard container justifyContent="center" xs={12} p={2}>
<Grid container xs={12} justifyContent="space-between">
{(getRoleFromUrl() === "KillHouse" ||
getRoleFromUrl() === "ChainCompany") && (
<Grid>
<Button
variant="contained"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "پرداخت تعرفه",
content: <ProvincePaymentPayDept />,
})
);
}}
>
پرداخت تعرفه
</Button>
</Grid>
)}
{/* {(getRoleFromUrl() === "KillHouse" ||
getRoleFromUrl() === "ChainCompany") && (
<Grid>
<Button variant="contained" href="https://zarinp.al/ravandno">
پرداخت تعرفه
</Button>
</Grid>
)} */}
</Grid>
<ResponsiveTable
isDashboard
noPagination
title="اطلاعات کلی تعرفه"
columns={[
"کل تعرفه (ریال)",
"کل تعرفه پرداخت شده (ریال)",
"تخفیف (ریال)",
"کل تعرفه پرداخت نشده (ریال)",
]}
data={[
[
// Math.ceil(
// parentCompanyPaymentByWeightOverview?.wageInfo?.totalWeight
// )?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.totalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.totalPaidWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.off
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.totalUnpaidWage
)?.toLocaleString(),
],
]}
/>
{getRoleFromUrl() !== "KillHouse" && (
<ResponsiveTable
noPagination
title="جزئیات کلی تعرفه"
columns={[
"کل وزن زنده کشتار داخل استان (کیلوگرم)",
"وزن لاشه کشتار داخل استان (کیلوگرم)",
"وزن توزیع لاشه داخل استان (کیلوگرم)",
"تعرفه توزیع لاشه داخل استان (ریال)",
"وزن توزیع لاشه به خارج استان (کیلوگرم)",
"تعرفه توزیع لاشه خارج استان (ریال)",
"وزن لاشه ورودی به استان (کیلوگرم)",
"تعرفه لاشه ورودی به استان (ریال)",
"وزن مرغ زنده ورودی به استان (کیلوگرم)",
"تعرفه مرغ زنده ورودی به استان (ریال)",
]}
data={[
[
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalProvinceLiveWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalProvinceCarcassesWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.provinceKillRequestsTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.provinceKillRequestsTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsOutProvinceCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsOutProvinceCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsLiveTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsLiveTotalWage
)?.toLocaleString(),
],
]}
/>
)}
</Grid>
<Grid container justifyContent="center" xs={12} mt={2} mb={2}>
<ResponsiveTable
noPagination
title="مبنای تعرفه به ازای هر کیلوگرم (ریال)"
columns={[
"تعرفه پایه توزیع لاشه درون استان",
"تعرفه پایه توزیع لاشه خارج استان",
"تعرفه پایه خرید مرغ زنده خارج از استان",
"تعرفه پایه لاشه خارج از استان",
]}
data={[
[
parentCompanyPaymentByWeightOverview?.wageInfo?.provinceLiveWageAmount?.toLocaleString(),
parentCompanyPaymentByWeightOverview?.wageInfo?.freeSellCarcessesWeightAmount?.toLocaleString(),
parentCompanyPaymentByWeightOverview?.wageInfo?.freeBuyingLiveWeightAmount?.toLocaleString(),
parentCompanyPaymentByWeightOverview?.wageInfo?.freeBuyingCarcessesWeightAmount?.toLocaleString(),
],
]}
/>
</Grid>
<Grid container justifyContent="center" xs={12} mt={1}>
<ResponsiveTable
noPagination={getRoleFromUrl() === "KillHouse"}
paginated={getRoleFromUrl() !== "KillHouse"}
title="جزئیات"
columns={
getRoleFromUrl() === "KillHouse"
? [
"کل تعرفه (ریال)",
"کل تعرفه پرداخت شده (ریال)",
"تخفیف (ریال)",
"کل تعرفه پرداخت نشده (ریال)",
"کل وزن زنده کشتار داخل استان (کیلوگرم)",
"وزن لاشه کشتار داخل استان (کیلوگرم)",
"وزن توزیع لاشه داخل استان (کیلوگرم)",
"تعرفه توزیع لاشه داخل استان (ریال)",
"وزن توزیع لاشه به خارج استان (کیلوگرم)",
"تعرفه توزیع لاشه خارج استان (ریال)",
"وزن لاشه ورودی به استان (کیلوگرم)",
"تعرفه لاشه ورودی به استان (ریال)",
"وزن مرغ زنده ورودی به استان (کیلوگرم)",
"تعرفه مرغ زنده ورودی به استان (ریال)",
]
: [
"ردیف",
"کشتارگاه",
"کل تعرفه (ریال)",
"کل تعرفه پرداخت شده (ریال)",
"تخفیف (ریال)",
"کل تعرفه پرداخت نشده (ریال)",
"کل وزن زنده کشتار داخل استان (کیلوگرم)",
"وزن لاشه کشتار داخل استان (کیلوگرم)",
"وزن توزیع لاشه داخل استان (کیلوگرم)",
"تعرفه توزیع لاشه داخل استان (ریال)",
"وزن توزیع لاشه به خارج استان (کیلوگرم)",
"تعرفه توزیع لاشه خارج استان (ریال)",
"وزن لاشه ورودی به استان (کیلوگرم)",
"تعرفه لاشه ورودی به استان (ریال)",
"وزن مرغ زنده ورودی به استان (کیلوگرم)",
"تعرفه مرغ زنده ورودی به استان (ریال)",
]
}
data={
getRoleFromUrl() === "KillHouse"
? [
[
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.totalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalPaidWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo?.off
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalUnpaidWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalProvinceLiveWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.totalProvinceCarcassesWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.provinceKillRequestsTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.provinceKillRequestsTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsOutProvinceCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsOutProvinceCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsCarcasesTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsCarcasesTotalWage
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsLiveTotalWeight
)?.toLocaleString(),
Math.ceil(
parentCompanyPaymentByWeightOverview?.wageInfo
?.freeBarsLiveTotalWage
)?.toLocaleString(),
],
]
: tableDataOfWages
}
/>
</Grid>
<ProvinceWagePaymentTransactions province={province} />
</Grid>
);
};

View File

@@ -0,0 +1,72 @@
import { Switch } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
import { policyChangeKillhouseDirectBuyService } from "../../services/policy-change-killhouse-direct-buy";
import { provinceGetKillhousesService } from "../../services/province-get-killhouses";
import { BackButton } from "../../../../components/back-button/BackButton";
export const PolicyKillhouseDirectBuy = () => {
const dispatch = useDispatch();
const { provinceGetKillhouses } = useSelector((state) => state.provinceSlice);
const [data, setData] = useState();
useEffect(() => {
dispatch(provinceGetKillhousesService());
}, []);
useEffect(() => {
const d = provinceGetKillhouses?.map((item) => {
return [
item.killHouse.name,
<Switch
key="kkk"
checked={item.allow}
onChange={(event) => {
dispatch(
policyChangeKillhouseDirectBuyService({
direct_buying_key: item.key,
allow: event.target.checked,
})
).then((r) => {
dispatch(provinceGetKillhousesService());
});
// setChecked(event.target.checked);
}}
name="checked"
color="primary"
/>,
<Switch
key="kkk"
checked={item.exportStatus}
onChange={(event) => {
dispatch(
policyChangeKillhouseDirectBuyService({
direct_buying_key: item.key,
export_status: event.target.checked,
})
).then((r) => {
dispatch(provinceGetKillhousesService());
});
// setChecked(event.target.checked);
}}
name="checked"
color="primary"
/>,
];
});
setData(d);
}, [provinceGetKillhouses]);
return (
<Grid container>
<BackButton />
<AdvancedTable
name="اجازه خرید مستقیم"
columns={["کشتارگاه/کشتارکن", "خرید مستقیم", "صادرات"]}
data={data}
/>
</Grid>
);
};

View File

@@ -0,0 +1,60 @@
import { Switch } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { BackButton } from "../../../../components/back-button/BackButton";
import { Grid } from "../../../../components/grid/Grid";
import { policyChangeKillhouseGuildsService } from "../../services/policy-change-killhouse-guilds";
import { provinceGetKillhousesGuildsService } from "../../services/province-get-killhouses-guilds";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const PolicyKillhouseGuilds = () => {
const dispatch = useDispatch();
const { provinceGetKillhousesGuilds } = useSelector(
(state) => state.provinceSlice
);
const [data, setData] = useState();
useEffect(() => {
dispatch(provinceGetKillhousesGuildsService());
}, []);
useEffect(() => {
const d = provinceGetKillhousesGuilds?.map((item, i) => {
return [
item.killHouse?.name,
item.killHouse?.killer ? "کشتارکن" : "کشتارگاه",
<Switch
key={i}
checked={item.allow}
onChange={(event) => {
dispatch(
policyChangeKillhouseGuildsService({
register_guilds_key: item.key,
allow: event.target.checked,
})
).then((r) => {
dispatch(provinceGetKillhousesGuildsService());
});
// setChecked(event.target.checked);
}}
name="checked"
color="primary"
/>,
];
});
setData(d);
}, [provinceGetKillhousesGuilds]);
return (
<Grid container>
<BackButton />
<ResponsiveTable
paginated
title="دسترسی به اصناف"
columns={["کشتارگاه/کشتارکن", "ماهیت", "دسترسی"]}
data={data}
customColors={[{ name: "ماهیت", color: "green" }]}
/>
</Grid>
);
};

View File

@@ -0,0 +1,53 @@
import { Switch } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
import { policyChangePoultryChooseSlaughterService } from "../../services/policy-change-poultry-choose-slaughter";
import { provinceGetPoultriesService } from "../../services/province-get-poultries";
export const PolicyPoultryChooseSlaughter = () => {
const dispatch = useDispatch();
const { provinceGetPoultries } = useSelector((state) => state.provinceSlice);
const [data, setData] = useState();
useEffect(() => {
dispatch(provinceGetPoultriesService());
}, []);
useEffect(() => {
const d = provinceGetPoultries?.map((item) => {
return [
item.name,
<Switch
key="kkk"
checked={item.allowState}
onChange={(event) => {
dispatch(
policyChangePoultryChooseSlaughterService({
poultry_key: item.key,
allow: event.target.checked,
})
).then(() => {
dispatch(provinceGetPoultriesService());
});
// setChecked(event.target.checked);
}}
name="checked"
color="primary"
/>,
];
});
setData(d);
}, [provinceGetPoultries]);
return (
<Grid container>
<AdvancedTable
name="دسترسی مرغداران جهت انتخاب کشتارگاه"
columns={["مرغدار", "دسترسی"]}
data={data}
/>
</Grid>
);
};

View File

@@ -0,0 +1,55 @@
import { Switch } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
import { policyChangePoultryFreeSalesService } from "../../services/policy-change-poultry-free-sales";
import { provinceGetPoultriesService } from "../../services/province-get-poultries";
import { BackButton } from "../../../../components/back-button/BackButton";
export const PolicyPoultryFreeSale = () => {
const dispatch = useDispatch();
const { provinceGetPoultries } = useSelector((state) => state.provinceSlice);
const [data, setData] = useState();
useEffect(() => {
dispatch(provinceGetPoultriesService());
}, []);
useEffect(() => {
const d = provinceGetPoultries?.map((item) => {
return [
item.name,
<Switch
key="kkk"
checked={item.allowSellState}
onChange={(event) => {
dispatch(
policyChangePoultryFreeSalesService({
poultry_key: item.key,
allow: event.target.checked,
})
).then((r) => {
dispatch(provinceGetPoultriesService());
});
// setChecked(event.target.checked);
}}
name="checked"
color="primary"
/>,
];
});
setData(d);
}, [provinceGetPoultries]);
return (
<Grid container>
<BackButton />
<AdvancedTable
name="دسترسی مرغداران جهت فروش آزاد"
columns={["مرغدار", "دسترسی"]}
data={data}
/>
</Grid>
);
};

View File

@@ -0,0 +1,647 @@
import { Fragment, useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { useParams, useSearchParams } from "react-router-dom";
import {
Autocomplete,
Checkbox,
CircularProgress,
Tab,
Tabs,
TextField,
Typography,
IconButton,
} from "@mui/material";
import { useDispatch } from "react-redux";
import axios from "axios";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import ToggleOffOutlinedIcon from "@mui/icons-material/ToggleOffOutlined";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import SearchIcon from "@mui/icons-material/Search";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { formatJustDate } from "../../../../utils/formatTime";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
import { AppContext } from "../../../../contexts/AppContext";
import { getPoultryTransportByCodeService } from "../../services/get-poultry-transport-by-code";
import { getPoultryTransportProductsByCodeService } from "../../services/get-poultry-transport-products-by-code";
import { getPoultryTransportDashboardByCodeService } from "../../services/get-poultry-transport-dashboard-by-code";
export const PoultriesDetails = () => {
const { key } = useParams();
const [searchParams] = useSearchParams();
const from = searchParams.get("from");
const dispatch = useDispatch();
const [tabValue, setTabValue] = useState("1");
const [tableData, setTableData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(10);
const [isLoading, setIsLoading] = useState(false); // eslint-disable-line no-unused-vars
const [error, setError] = useState(null);
const [withDate, setWithDate] = useState(true);
const [productOptions, setProductOptions] = useState([]);
const [selectedProduct, setSelectedProduct] = useState("");
const [isProductLoading, setIsProductLoading] = useState(false);
const [searchValue, setSearchValue] = useState("");
const [searchInput, setSearchInput] = useState("");
const [dashboardData, setDashboardData] = useState(null);
const [provinceOptions, setProvinceOptions] = useState([]);
const [selectedProvince, setSelectedProvince] = useState("");
const [isProvinceLoading, setIsProvinceLoading] = useState(false);
console.log(from);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const columns = [
"ردیف",
"کد رهگیری",
"محصول",
"اقلام",
"مقدار",
"واحد",
"تاریخ",
"مقصد",
"شناسه مقصد",
"استان مقصد",
"شهرستان مقصد",
"مبدا",
"شناسه مبدا",
"استان مبدا",
"شهرستان مبدا",
"نوع حمل",
"مقصد قبلی",
"تغییر مقصد",
"کد رهگیری خودرو",
"تاریخ تخلیه",
"تخلیه",
];
const formatNumber = (value) => {
if (value === null || value === undefined) {
return "-";
}
if (typeof value === "number") {
return value.toLocaleString("fa-IR");
}
return value;
};
const safeValue = (value) => {
if (value === null || value === undefined || value === "") {
return "-";
}
return value;
};
const formatPercent = (value) => {
if (value === null || value === undefined) {
return "-";
}
const numericValue = Number(value);
if (Number.isNaN(numericValue)) {
return "-";
}
return `${numericValue.toFixed(1)}%`;
};
const formatDateTimeFromIso = (value) => {
if (!value) return "-";
const dateObj = new Date(value);
if (Number.isNaN(dateObj.getTime())) return "-";
const date = dateObj.toLocaleDateString("fa-IR");
const time = dateObj.toLocaleTimeString("fa-IR", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
return `${date} ${time}`;
};
const dashboardColumns = [
"محصول",
"تعداد بار",
"حجم بار (کیلوگرم)",
"تعداد بار داخل استان",
"حجم بار داخل استان (کیلوگرم)",
"درصد داخل استان",
"تعداد بار خارج استان",
"حجم بار خارج استان (کیلوگرم)",
"درصد خارج استان",
"آخرین بروزرسانی",
];
const handleTabChange = (event, newValue) => {
setTabValue(newValue);
setSelectedProduct("");
setPage(1);
};
const handleSubmit = async (event) => {
event.preventDefault();
const normalizedSearch = searchInput.trim();
setSearchValue(normalizedSearch);
setSearchInput(normalizedSearch);
setPage(1);
};
useEffect(() => {
setPage(1);
}, [
selectedDate1,
selectedDate2,
withDate,
selectedProduct,
searchValue,
tabValue,
selectedProvince,
]);
useEffect(() => {
const isMounted = { current: true };
const fetchProvinces = async () => {
try {
setIsProvinceLoading(true);
const { data } = await axios.get("iran_province/");
if (!isMounted.current) {
return;
}
const names =
data?.map?.((item) => item?.name).filter((name) => !!name) ?? [];
setProvinceOptions(names);
} catch (err) {
console.error("Failed to fetch provinces:", err);
} finally {
if (isMounted.current) {
setIsProvinceLoading(false);
}
}
};
fetchProvinces();
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
if (!key) return;
const isMounted = { current: true };
const fetchProducts = async () => {
try {
setIsProductLoading(true);
const type = tabValue === "1" ? "in" : "out";
const response = await dispatch(
getPoultryTransportProductsByCodeService({
code: key,
type: type,
date1: withDate && selectedDate1 ? selectedDate1 : "",
date2: withDate && selectedDate2 ? selectedDate2 : "",
search: searchValue || "",
from: from,
})
);
if (!isMounted.current) {
return;
}
if (response.error) {
console.error("Failed to fetch products:", response.error);
return;
}
const products = Array.isArray(response.payload?.data?.products)
? response.payload.data.products
: [];
setProductOptions(products);
} catch (err) {
console.error("Failed to fetch product types:", err);
} finally {
if (isMounted.current) {
setIsProductLoading(false);
}
}
};
fetchProducts();
return () => {
isMounted.current = false;
};
}, [
key,
tabValue,
selectedDate1,
selectedDate2,
withDate,
searchValue,
dispatch,
]);
useEffect(() => {
if (!key) return;
const isMounted = { current: true };
const fetchDashboardData = async () => {
try {
const type = tabValue === "1" ? "in" : "out";
const response = await dispatch(
getPoultryTransportDashboardByCodeService({
code: key,
type: type,
date1: withDate && selectedDate1 ? selectedDate1 : "",
date2: withDate && selectedDate2 ? selectedDate2 : "",
search: searchValue || "",
product: selectedProduct || "",
province: selectedProvince || "",
from: from,
})
);
if (!isMounted.current) {
return;
}
if (!response.error && response.payload?.data) {
setDashboardData(response.payload.data);
}
} catch (err) {
console.error("Failed to fetch dashboard data:", err);
}
};
fetchDashboardData();
return () => {
isMounted.current = false;
};
}, [
key,
tabValue,
selectedDate1,
selectedDate2,
withDate,
selectedProduct,
searchValue,
selectedProvince,
dispatch,
]);
useEffect(() => {
if (!key) return;
const isMounted = { current: true };
const fetchTransportData = async () => {
try {
setError(null);
setIsLoading(true);
const type = tabValue === "1" ? "in" : "out";
const response = await dispatch(
getPoultryTransportByCodeService({
code: key,
type: type,
page: page,
page_size: perPage,
date1: withDate && selectedDate1 ? selectedDate1 : "",
date2: withDate && selectedDate2 ? selectedDate2 : "",
search: searchValue || "",
product: selectedProduct || "",
province: selectedProvince || "",
from: from,
})
);
if (!isMounted.current) {
return;
}
if (response.error) {
setError(response.error);
return;
}
const responseData = response.payload?.data ?? { results: [] };
const results = responseData?.results ?? [];
const mappedTableData = results.map((item, index) => [
safeValue(formatNumber(index + 1 + (page - 1) * perPage)),
<CheckCleanceCode
key={item?.tracking || index}
clearanceCode={item?.tracking}
/>,
safeValue(item?.product),
safeValue(item?.items),
safeValue(formatNumber(item?.quantity)),
safeValue(item?.unit),
safeValue(formatJustDate(item?.date)),
safeValue(item?.destination),
safeValue(item?.jihadiDestination),
safeValue(item?.destinationProvince),
safeValue(item?.destinationCity),
safeValue(item?.origin),
safeValue(item?.jihadiOrigin),
safeValue(item?.originProvince),
safeValue(item?.originCity),
safeValue(
item?.originProvince === item?.destinationProvince
? "داخل استان"
: "خارج استان"
),
safeValue(item?.destinationPrev),
safeValue(item?.destinationChanged),
safeValue(item?.carTrackingCode),
safeValue(formatJustDate(item?.unloadingDate)),
safeValue(item?.unloading),
]);
setTableData(mappedTableData);
setTotalRows(responseData?.count ?? 0);
} catch (err) {
console.error("Failed to fetch transport data:", err);
if (!isMounted.current) {
return;
}
setError(err);
} finally {
if (isMounted.current) {
setIsLoading(false);
}
}
};
fetchTransportData();
return () => {
isMounted.current = false;
};
}, [
key,
tabValue,
page,
perPage,
selectedDate1,
selectedDate2,
withDate,
selectedProduct,
searchValue,
selectedProvince,
dispatch,
]);
const handlePageChange = (newPage) => {
setPage(newPage);
};
const handlePerRowsChange = (newPerPage) => {
setPerPage(newPerPage);
setPage(1);
};
return (
<Fragment>
<Grid container justifyContent="center" xs={12} mt={2}>
<Grid
container
alignItems="center"
justifyContent="center"
xs={12}
mt={2}
>
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={tabValue}
onChange={handleTabChange}
>
<Tab label="بارهای ورودی" value="1" />
<Tab label="بارهای خروجی" value="2" />
</Tabs>
</Grid>
{dashboardData && (
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={dashboardColumns}
data={[
[
safeValue(dashboardData?.product),
safeValue(formatNumber(dashboardData?.bars)),
safeValue(formatNumber(dashboardData?.totalBarsWight)),
safeValue(formatNumber(dashboardData?.inputBars)),
safeValue(formatNumber(dashboardData?.totalInputBarsWight)),
formatPercent(dashboardData?.totalInputBarsPercent),
safeValue(formatNumber(dashboardData?.outputBars)),
safeValue(formatNumber(dashboardData?.totalOutputBarsWight)),
formatPercent(dashboardData?.totalOutputBarsPercent),
formatDateTimeFromIso(dashboardData?.lastUpdate),
],
]}
title="داشبورد"
/>
</Grid>
)}
<Grid
container
alignItems="center"
justifyContent="center"
gap={2}
xs={12}
mt={2}
px={2}
>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "5px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
maxWidth: "fit-content",
}}
alignItems="center"
justifyContent="center"
>
<Checkbox
icon={<ToggleOffOutlinedIcon />}
checkedIcon={<ToggleOnIcon />}
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
size="large"
/>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
if (!e) return;
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
if (!e) return;
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<Grid
container
alignItems="center"
justifyContent="center"
gap={1}
sx={{ maxWidth: "fit-content" }}
>
<Autocomplete
options={provinceOptions}
loading={isProvinceLoading}
value={selectedProvince || null}
onChange={(_, value) => {
setSelectedProvince(value || "");
}}
noOptionsText="استانی یافت نشد"
renderInput={(params) => (
<TextField
{...params}
label={tabValue === "2" ? "استان مقصد" : "استان مبدا"}
size="small"
sx={{ width: { xs: 200, md: 240 } }}
InputProps={{
...params.InputProps,
endAdornment: (
<>
{isProvinceLoading ? (
<CircularProgress color="inherit" size={16} />
) : null}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
clearOnEscape
/>
<Autocomplete
options={productOptions}
loading={isProductLoading}
value={selectedProduct || null}
onChange={(_, value) => {
setSelectedProduct(value || "");
}}
noOptionsText="محصولی یافت نشد"
renderInput={(params) => (
<TextField
{...params}
label="نوع محصول"
size="small"
sx={{ width: { xs: 200, md: 240 } }}
InputProps={{
...params.InputProps,
endAdornment: (
<>
{isProductLoading ? (
<CircularProgress color="inherit" size={16} />
) : null}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
clearOnEscape
/>
<Grid
item
component="form"
onSubmit={handleSubmit}
sx={{ width: { xs: 220, md: 260 }, display: "flex" }}
>
<TextField
value={searchInput}
onChange={(event) => setSearchInput(event.target.value)}
label="جستجو"
size="small"
variant="outlined"
sx={{ flexGrow: 1 }}
/>
<IconButton
type="submit"
color="primary"
aria-label="جستجو"
sx={{ ml: 1 }}
>
<SearchIcon />
</IconButton>
</Grid>
</Grid>
</Grid>
<Grid
container
justifyContent="center"
alignItems="center"
px={2}
xs={12}
mt={2}
>
{error && (
<Grid container justifyContent="center" xs={12}>
<Typography variant="body1" color="error">
خطا در دریافت اطلاعات
</Typography>
</Grid>
)}
{!error && (
<ResponsiveTable
columns={columns}
data={tableData}
title={tabValue === "1" ? "بارهای ورودی" : "بارهای خروجی"}
paginated={false}
handlePageChange={handlePageChange}
handlePerRowsChange={handlePerRowsChange}
totalRows={totalRows}
page={page}
perPage={perPage}
/>
)}
</Grid>
</Grid>
</Fragment>
);
};

View File

@@ -0,0 +1,72 @@
import { Button, TextField, Typography } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { provinceAcceptSlaughterFreeBuyService } from "../../../slaughter-house/services/province-accept-slaughter-free-buy";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const ProvinceAcceptDirectBuy = ({ item, updateTable }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const [text, setText] = useState(item.killCapacity);
const handleTextChange = (event) => {
setText(event.target.value);
};
return (
<Grid container direction="column" gap={SPACING.TINY} width="100%">
<Typography variant="body1" color="error">
مانده در سالن مرغدار:{" "}
{item?.poultry?.leftOverOwnHatching?.toLocaleString()}
</Typography>
<TextField
label="تعداد تایید شده"
variant="outlined"
type="number"
fullWidth
value={text}
onChange={handleTextChange}
/>
<Button
variant="contained"
disabled={parseInt(text) > item?.poultry?.leftOverOwnHatching || !text}
fullWidth
onClick={() => {
dispatch(
provinceAcceptSlaughterFreeBuyService({
state: "accepted",
kill_request_key: item.key,
quantity: text,
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,75 @@
import { Card, IconButton } from "@mui/material";
import { useEffect, useState } from "react";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { useDispatch, useSelector } from "react-redux";
import { provinceGetActiveRequestsService } from "../../services/province-get-active-requests";
import { useNavigate } from "react-router-dom";
import { ROUTE_PROVINCE_FILE } from "../../../../routes/routes";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { format } from "date-fns-jalali";
export const ProvinceActiveRequests = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { provinceGetActiveRequests } = useSelector(
(state) => state.provinceSlice
);
const [dataTable, setDataTable] = useState([]);
useEffect(() => {
dispatch(provinceGetActiveRequestsService());
}, []);
useEffect(() => {
const d = provinceGetActiveRequests?.map((item, i) => {
return [
i + 1,
item?.poultryRequest?.orderCode,
format(new Date(item?.poultryRequest?.createDate), "yyyy/MM/dd"),
format(new Date(item?.poultryRequest?.sendDate), "yyyy/MM/dd"),
item?.poultryRequest?.process?.poultry?.poultryName,
item?.poultryRequest?.process?.poultry?.poultryMobile,
item?.poultryRequest?.process?.poultry?.poultryCity,
item?.poultryRequest?.process?.poultry?.poultryProvince,
item?.poultryRequest?.process?.poultry?.age,
item?.poultryRequest?.process?.poultry?.poultryQuantity,
<IconButton
key={i}
aria-label="delete"
color="primary"
onClick={() => {
navigate(
ROUTE_PROVINCE_FILE +
item?.poultryRequest?.process?.poultry?.poultryRequestId
);
}}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [provinceGetActiveRequests]);
return (
<Card>
<AdvancedTable
name={"درخواست های فعال"}
columns={[
"ردیف",
"کد سفارش",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"مرغدار",
"تلفن مرغدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"مشاهده",
]}
data={dataTable}
/>
</Card>
);
};

View File

@@ -0,0 +1,128 @@
import { Autocomplete, Button, TextField } from "@mui/material";
import { useFormik } from "formik";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { provinceGetBuyersService } from "../../services/province-get-buyers";
export const ProvinceAddBuyerAutoAllocation = ({
dataTable,
setDataTable,
setAddedRows,
poultryIndex,
}) => {
const dispatch = useDispatch();
const [selectedCountry, setSelectedCountry] = useState(null);
const { provinceGetBuyers } = useSelector((state) => state.provinceSlice);
const formik = useFormik({
initialValues: {
num: "",
},
onSubmit: (values) => {
// console.log(values);
},
});
useEffect(() => {
dispatch(provinceGetBuyersService());
}, []);
return (
<Grid contianer width="70%">
<Autocomplete
id="country-select"
options={provinceGetBuyers?.map((item) => {
return {
// disabled: item.debt,
name: item.debt ? `${item.name} (بدهکار)` : item.name,
obj: item,
};
})}
getOptionLabel={(option) => option.name}
getOptionDisabled={(option) => option.disabled}
value={selectedCountry}
onChange={(event, newValue) => {
setSelectedCountry(newValue);
}}
renderInput={(params) => (
<TextField {...params} label="انتخاب خریدار" variant="outlined" />
)}
/>
<div style={{ marginTop: "10px" }}></div>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="num"
label="تعداد مرغ"
variant="outlined"
value={formik.values.num}
error={formik.touched.num ? Boolean(formik.errors.num) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.num && Boolean(formik.errors.num)
? formik.errors.num
: null
}
/>
<div style={{ marginTop: "10px" }}></div>
<Button
variant="contained"
fullWidth
onClick={() => {
setAddedRows((prevState) => {
return [
...prevState,
{
kill_house_key: selectedCountry?.obj?.key,
poultry_request_key: dataTable[poultryIndex].poultryRequestKey,
quantity: Number(formik.values.num),
},
];
});
setDataTable((prevState) => {
// let edited = Array.from(prevState);
let edited = deepCopy(prevState);
edited[poultryIndex].allocations.push({
allocationKey: null,
type: selectedCountry?.obj?.killer,
buyerName: selectedCountry?.name,
buyerMobile: selectedCountry?.obj?.phone,
buyerAddress: selectedCountry?.obj?.systemAddress?.city?.name,
allocationQuantity: Number(formik.values.num),
allocationPercent: 0,
KillingPlace: selectedCountry?.obj?.killPlace,
});
edited[poultryIndex].allocationsSum = edited[
poultryIndex
].allocations?.reduce((total, item) => {
return total + item.allocationQuantity;
}, 0);
return edited;
});
dispatch(CLOSE_MODAL());
}}
>
ثبت
</Button>
</Grid>
);
};
function deepCopy(obj) {
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
newObj[key] = deepCopy(obj[key]);
} else {
newObj[key] = obj[key];
}
}
return newObj;
}

View File

@@ -0,0 +1,299 @@
import React, { useContext, useEffect, useState } from "react";
import {
TextField,
Button,
Grid,
IconButton,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { useDispatch } from "react-redux";
import {
provinceDeleteAgeMessagesService,
provinceGetAgeMessagesService,
provinceSubmitAgeMessagesService,
} from "../../services/province-age-message";
import { formatJustDate } from "../../../../utils/formatTime";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import TuneIcon from "@mui/icons-material/Tune";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { ProvinceEditAgeMessage } from "../province-edit-age-message/ProvinceEditAgeMessage";
import { AppContext } from "../../../../contexts/AppContext";
export const ProvinceAgeMessage = () => {
const [tableData, setTableData] = useState([]);
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const validationSchema = Yup.object({
ageRange: Yup.number()
.required("بازه سنی ضروری است")
.max(99, "عدد دورقمی وارد کنید!"),
lossPercent: Yup.number()
.required("درصد افت ضروری است")
.max(99, "عدد دورقمی وارد کنید!"),
message: Yup.string()
.required("پیغام ضروری است")
.max(500, "حداکثر 500 کاراکتر"),
});
const formik = useFormik({
initialValues: {
ageRange: "",
message: "",
lossPercent: "",
},
validationSchema,
onSubmit: (values, { resetForm }) => {
dispatch(
provinceSubmitAgeMessagesService({
message: values.message,
poultry_age: values.ageRange,
losses_percent: values.lossPercent,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
fetchData();
resetForm();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
const fetchData = () => {
dispatch(provinceGetAgeMessagesService()).then((r) => {
const d = r.payload.data?.map((item, i) => {
return [
i + 1,
formatJustDate(item?.createDate),
item?.lossesPercent,
item?.poultryAge,
item?.message,
<Grid justifyContent="center" alignItems="center" key={i}>
<ProvinceAgeMessageActions
item={item}
onDelete={() => {
dispatch(provinceDeleteAgeMessagesService(item?.key)).then(
(r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
fetchData();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
}
);
}}
onEdit={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش خریدار",
content: (
<ProvinceEditAgeMessage
isEdit
item={item}
fetchData={fetchData}
/>
),
})
);
}}
/>
</Grid>,
];
});
setTableData(d);
});
};
const ProvinceAgeMessageActions = ({ onDelete, onEdit }) => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleDelete = () => {
onDelete();
handleClose();
};
const handleEdit = () => {
onEdit();
handleClose();
};
return (
<Grid>
<Tooltip title="تنظیمات">
<IconButton
color="primary"
className="avicultureActiveRequestsBtn"
onClick={handleOpen}
>
<TuneIcon />
</IconButton>
</Tooltip>
<Popover
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<List sx={{ minWidth: 140, p: 1 }}>
<ListItemButton onClick={handleEdit} sx={{ py: 1 }}>
<ListItemIcon>
<EditIcon color="primary" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
ویرایش
</Typography>
}
/>
</ListItemButton>
<ListItemButton onClick={handleDelete} sx={{ py: 1 }}>
<ListItemIcon>
<DeleteIcon color="error" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="textPrimary">
حذف
</Typography>
}
/>
</ListItemButton>
</List>
</Popover>
</Grid>
);
};
useEffect(() => {
fetchData();
}, [dispatch]);
return (
<form onSubmit={formik.handleSubmit}>
<Grid container justifyContent="space-between" xs={12} spacing={2}>
<Grid container spacing={2} xs={12} sm={6}>
<Grid item xs={6}>
<TextField
fullWidth
id="ageRange"
name="ageRange"
label="بازه سنی"
variant="outlined"
value={formik.values.ageRange}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.ageRange && Boolean(formik.errors.ageRange)}
helperText={formik.touched.ageRange && formik.errors.ageRange}
/>
</Grid>
<Grid item xs={6}>
<TextField
fullWidth
id="lossPercent"
name="lossPercent"
label="درصد افت"
variant="outlined"
value={formik.values.lossPercent}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.lossPercent && Boolean(formik.errors.lossPercent)
}
helperText={
formik.touched.lossPercent && formik.errors.lossPercent
}
/>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
multiline
rows={5}
id="message"
name="message"
label="پیغام"
variant="outlined"
value={formik.values.message}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.message && Boolean(formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
/>
</Grid>
<Grid item xs={12}>
<Button type="submit" variant="contained" color="primary" fullWidth>
ثبت
</Button>
</Grid>
</Grid>
<Grid container spacing={2} xs={12} sm={6}>
<ResponsiveTable
title="پیغام بازه سنی"
paginated
columns={[
"ردیف",
"تاریخ ثبت",
"درصد افت",
"بازه سنی",
"متن پیام",
"عملیات",
]}
data={tableData}
/>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,210 @@
import { Autocomplete, Button, TextField, Typography } from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceAddCarToKillhouseService } from "../../services/province-add-car-to-killhouse";
import { provinceGetCars } from "../../services/province-get-cars";
import { provinceGetOnlyKillHousesService } from "../../services/province-get-only-kill-houses";
import { provinceRemoveCarFromKillhouseService } from "../../services/province-remove-car-from-killhouse";
const validationSchema = Yup.object().shape({
// add any other validation rules that you need
autocompleteField: Yup.string().required("This field is required"),
});
export const ProvinceAllocateCarsForm = ({ killHouseList, driverKey }) => {
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
autocompleteField: "",
},
validationSchema,
onSubmit: (values) => {
if (!values?.autocompleteField || !driverKey) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا تمام فیلدها را پر کنید",
severity: "error",
});
return;
}
dispatch(
provinceAddCarToKillhouseService({
kill_house_key: values.autocompleteField,
driver_key: driverKey,
})
).then((r) => {
if (r?.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(provinceGetCars());
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
const dispatch = useDispatch();
const [killhouses, setKillhouses] = useState([]);
const { provinceGetOnlyKillHouses } = useSelector(
(state) => state.provinceSlice
);
useEffect(() => {
if (getRoleFromUrl() === "KillHouse") {
dispatch(provinceGetOnlyKillHousesService("self_kill_houses"));
} else {
dispatch(provinceGetOnlyKillHousesService("role=ProvinceOperator"));
}
}, []);
useEffect(() => {
if (
!provinceGetOnlyKillHouses ||
!Array.isArray(provinceGetOnlyKillHouses)
) {
setKillhouses([]);
return;
}
const d = provinceGetOnlyKillHouses
.map((item) => {
if (!item) return null;
let prefix;
if (item.killer) {
prefix = "کشتارکن";
} else {
prefix = "کشتارگاه";
}
const name = `${prefix} ${item?.name || ""}`;
return { title: name, value: item?.key || "" };
})
.filter((item) => item !== null && item.value);
setKillhouses(d || []);
}, [provinceGetOnlyKillHouses]);
return (
<Grid>
<form onSubmit={formik.handleSubmit}>
<Grid
container
direction="column"
gap={SPACING.TINY}
mb={SPACING.SMALL}
>
<Autocomplete
id="autocompleteField"
options={killhouses || []}
getOptionLabel={(option) => option?.title || ""}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب کشتارگاه"
variant="outlined"
fullWidth
error={
formik.touched.autocompleteField &&
Boolean(formik.errors.autocompleteField)
}
helperText={
formik.touched.autocompleteField &&
formik.errors.autocompleteField
}
/>
)}
onChange={(e, value) =>
formik.setFieldValue("autocompleteField", value?.value || "")
}
value={killhouses?.find(
(o) => o.value === formik.values.autocompleteField
)}
/>
<Button fullWidth variant="contained" type="submit">
ثبت
</Button>
</Grid>
</form>
<Typography variant="body1" fontWeight={"bold"}>
کشتارگاه های خودرو
</Typography>
{killHouseList && Array.isArray(killHouseList) && killHouseList.length > 0
? killHouseList.map((item, i) => {
if (!item) return null;
return (
<Grid
container
gap={SPACING.TINY}
alignItems="center"
justifyContent="space-between"
style={{ borderBottom: "1px solid #DDD" }}
key={item.killHouseKey || i}
>
<Typography>
{i + 1}. {item?.killHouseName || ""}
</Typography>
<Button
color="error"
onClick={() => {
if (!item?.killHouseKey || !driverKey) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "اطلاعات ناقص است",
severity: "error",
});
return;
}
dispatch(
provinceRemoveCarFromKillhouseService({
kill_house_key: item.killHouseKey,
driver_key: driverKey,
})
).then((r) => {
if (r?.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(provinceGetCars());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
حذف
</Button>
</Grid>
);
})
: null}
</Grid>
);
};

View File

@@ -0,0 +1,239 @@
import {
Button,
IconButton,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import { useContext, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { provinceCloseAllocationService } from "../../services/province-close-allocation";
import { provinceGetAllRequests } from "../../services/province-get-all-requests";
import ArchiveIcon from "@mui/icons-material/Archive";
import ConstructionIcon from "@mui/icons-material/Construction";
import { ProvinceAllocation } from "../../../file/components/province-allocation/ProvinceAllocation";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import TuneIcon from "@mui/icons-material/Tune";
import { ProvincePoultryRequestEnterConfirmationCode } from "../province-poultry-request-enter-confirmation-code/ProvincePoultryRequestEnterConfirmationCode";
export const ProvinceAllocateOperations = ({
item,
remainQuantity,
getItemFreeSaleInProvince,
updateTable,
}) => {
const dispatch = useDispatch();
const [openNotif, , selectedDate1] = useContext(AppContext);
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const id = open ? "ProvinceAllocateRequests" : undefined;
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<Grid>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div
style={{ padding: "20px", display: "flex", flexDirection: "column" }}
>
<Tooltip title="تخصیص" placement="right">
<IconButton
color="primary"
onClick={() => {
handleClose();
if (
item?.poultryRequest?.priceConfirmation === true &&
!item?.poultryRequest?.inputPriceConfirmationCode
) {
dispatch(
OPEN_MODAL({
title: "ورود کد احراز",
content: (
<ProvincePoultryRequestEnterConfirmationCode
updateTable={updateTable}
item={item?.poultryRequest}
/>
),
})
);
} else if (item?.provinceState === "pending") {
dispatch(
OPEN_MODAL({
title: "اطلاعیه سیستم",
content: (
<Grid>
<Typography variant="body1" color="secondary">
درخواست ابتدا باید توسط استان تایید شود!
</Typography>
<Grid container justifyContent="center">
<Grid
item
container
xs={12}
justifyContent="center"
mt={2}
>
<Button
size="small"
color="success"
variant="contained"
onClick={() => dispatch(CLOSE_MODAL())}
>
متوجه شدم
</Button>
</Grid>
</Grid>
</Grid>
),
})
);
} else {
dispatch(
DRAWER({
title: "انجام عملیات تخصیص",
// right: !(window.innerWidth <= 600),
top: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
size: 1000,
content: (
<ProvinceAllocation
remainQuantity={remainQuantity}
item={item}
id={
item?.poultryRequest?.process?.poultry
?.poultryRequestId
}
orginalQuantity={
item?.poultryRequest?.process?.poultry
?.poultryQuantity
}
file={item?.poultryRequest?.process}
getItemFreeSaleInProvince={getItemFreeSaleInProvince}
updateTable={updateTable}
/>
),
})
);
}
}}
>
<ConstructionIcon />
</IconButton>
</Tooltip>
<Tooltip title="اتمام" placement="right">
<IconButton
aria-label="delete"
color="primary"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "آیا مطمئن هستید؟",
content: (
<Grid container>
<Button
variant="contained"
onClick={() => {
dispatch(
provinceCloseAllocationService({
allocated_number:
item?.quantity?.allocatedNumber,
final_state: "archive",
key: item?.poultryRequest?.key,
})
).then((r) => {
dispatch(provinceGetAllRequests(selectedDate1));
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
),
})
);
}}
>
<ArchiveIcon />
</IconButton>
</Tooltip>
{/* <Tooltip title="مشاهده" placement="right">
<IconButton
aria-label="delete"
color="primary"
onClick={() => {
handleClose();
navigate(
ROUTE_PROVINCE_FILE +
item?.poultryRequest?.process?.poultry?.poultryRequestId
);
}}
>
<PlagiarismIcon />
</IconButton>
</Tooltip> */}
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,342 @@
import React, { useContext, useEffect, useState } from "react";
import {
Box,
Button,
Card,
CardContent,
IconButton,
TextField,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import { ProvinceAllocateOperations } from "../province-allocate-operations/ProvinceAllocateOperations";
import { ProvinceEditSendDate } from "../province-edit-send-date/ProvinceEditSendDate";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import EditIcon from "@mui/icons-material/Edit";
import { getPoultryRequestsTotalQuantityService } from "../../../city/services/get-poultry-requests-total-quantity";
import { motion } from "framer-motion";
import { RiSearchLine } from "react-icons/ri";
const cardVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
export const ProvinceAllocateRequests = () => {
const [, , selectedDate1, setSelectedDate1] = useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
dispatch(getPoultryRequestsTotalQuantityService(selectedDate1)).then(
async () => {
dispatch(LOADING_START());
const response = await axios.get(
`city_operator_check_request_new/?state=waiting&date=${selectedDate1}&page=${page}&page_size=${perPage}&search=filter&value=${
textValue ? textValue : ""
}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
}
);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const getItemFreeSaleInProvince = (item) => {
let sellType = "";
if (item?.poultryRequest?.freeSaleInProvince) {
sellType = "آزاد";
} else {
sellType = "دولتی";
}
return sellType;
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.poultryRequest?.orderCode,
getItemFreeSaleInProvince(item),
formatTime(item?.poultryRequest?.createDate),
<Grid
key={item?.poultryRequest?.orderCode}
container
alignItems="center"
justifyContent="center"
width="100px"
>
<Typography variant="caption">
{formatJustDate(item?.poultryRequest?.sendDate)}
</Typography>
<IconButton
size={"small"}
color="primary"
disabled={Number(item?.quantity?.allocatedNumber) > 0}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تاریخ درخواست کشتار",
content: (
<ProvinceEditSendDate
poultryRequestKey={item?.poultryRequest?.key}
/>
),
})
);
}}
>
<EditIcon fontSize="small" />
</IconButton>
</Grid>,
item?.poultryRequest?.freezing
? "انجماد"
: item?.poultryRequest?.export
? "صادرات"
: "عادی",
`${item?.poultryRequest?.process?.poultry?.poultryName}/${item?.poultryRequest?.process?.poultry?.poultryMobile}`,
item?.poultryRequest?.killHouseList.length
? item?.poultryRequest?.killHouseList?.join(" - ")
: "-",
`${item?.poultryRequest?.process?.poultry?.poultryProvince}/${item?.poultryRequest?.process?.poultry?.poultryCity}`,
item?.poultryRequest?.poultry?.cityOperator,
item?.poultryRequest?.killingAge,
item?.poultryRequest?.process?.poultry?.IndexWeight,
item?.poultryRequest?.process?.poultry?.totalWeight?.toLocaleString(),
item?.poultryRequest?.process?.poultry?.poultryQuantity?.toLocaleString(),
item?.poultryRequest?.amount?.toLocaleString() + " ﷼",
<Typography color="green" variant="caption" fontWeight="bold" key="sss">
{item?.quantity?.allocatedNumber?.toLocaleString()}
</Typography>,
item?.quantity?.returnedNumber?.toLocaleString(),
item?.quantity?.assignableNumber?.toLocaleString(),
<ProvinceAllocateOperations
key={i}
remainQuantity={
item?.poultryRequest?.process?.poultry?.poultryRemainQuantity
}
item={item}
getItemFreeSaleInProvince={getItemFreeSaleInProvince}
updateTable={updateTable}
/>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, selectedDate1, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`city_operator_check_request_new/?state=waiting&date=${selectedDate1}&page=${1}&page_size=${perPage}&search=filter&value=${
textValue ? textValue : ""
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const { poultryRequestsTotalQuantity } = useSelector(
(state) => state.citySlice
);
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
px={{
xs: 2,
sm: 0,
}}
>
<Grid>
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} size="small" />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
<Grid
container
sx={{
gap: {
xs: "8px 0px",
sm: "8px",
},
}}
mt={2}
justifyContent="center"
alignItems="center"
>
{[
{
label: "تعداد درخواست کشتار",
value: poultryRequestsTotalQuantity?.acceptedQuantity || 0,
},
{
label: "اعلام نیاز کشتارگاه ها",
value: poultryRequestsTotalQuantity?.killRequestsQuantity || 0,
},
{
label: "تخصیص داده شده",
value: poultryRequestsTotalQuantity?.allocatedQuantity || 0,
},
{
label: "قابل تخصیص",
value: poultryRequestsTotalQuantity?.assignableQuantity || 0,
},
{
label: "برگشت داده شده",
value: poultryRequestsTotalQuantity?.returnedQuantity || 0,
},
].map((item, index) => (
<Grid item sx={{ width: "250px" }} key={index}>
<motion.div
variants={cardVariants}
initial="hidden"
animate="visible"
transition={{ duration: 0.5, delay: index * 0.2 }}
>
<Card elevation={3} sx={{ borderRadius: 2 }}>
<CardContent>
<Typography color="red" variant="subtitle2" gutterBottom>
{item.label}
</Typography>
<Typography variant="h6" sx={{ fontSize: 18 }}>
{item.value?.toLocaleString()} قطعه
</Typography>
</CardContent>
</Card>
</motion.div>
</Grid>
))}
</Grid>
<Box
sx={{
width: "100%",
}}
px={{
xs: 1,
sm: 0,
}}
>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"کد سفارش",
"نوع فروش",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"نوع کشتار",
"مرغدار/تلفن",
"کشتارگاه انتخابی",
"استان/شهر",
"تعاونی",
"سن مرغ",
"میانگین وزنی",
"وزن تقریبی",
"تعداد",
"قیمت مرغدار",
"تخصیص داده شده",
"برگشت داده شده",
"قابل تخصیص",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مدیریت تخصیصات"
/>
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,218 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { useDispatch } from "react-redux";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import {
provinceDispensersAddSteward,
provinceDispensersGetAllStewards,
} from "../../services/province-dispensers-services";
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
TextField,
} from "@mui/material";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
export const ProvinceAllocateStewardGuild = ({
killHousekey,
updateTable,
type,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [stewardsOptions, setStewardsOptions] = useState([]);
const [value, setValue] = useState("forced");
const [userType, setUserType] = useState("steward");
const handleChangeRadioButton = (event) => {
setValue(event.target.value);
};
const handleChangeRadioButtonUserType = (event) => {
setUserType(event.target.value);
};
useEffect(() => {
dispatch(
provinceDispensersGetAllStewards({
role_type: "KillHouse",
key: killHousekey,
type: userType,
})
).then((r) => {
setStewardsOptions(r.payload.data);
});
}, [dispatch, userType]);
const validationSchema = Yup.object({
steward_key: Yup.string(),
allocation_limit: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
});
const formik = useFormik({
initialValues: {
steward_key: "",
allocation_limit: "",
},
validationSchema,
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={userType}
onChange={handleChangeRadioButtonUserType}
row
sx={{ alignItems: "center" }}
>
<FormControlLabel
value="steward"
control={<Radio />}
label="مباشرین"
/>
<FormControlLabel value="guild" control={<Radio />} label="اصناف" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12}>
<Autocomplete
style={{ width: "100%" }}
options={
stewardsOptions
? stewardsOptions.map((i) => ({
id: i.key,
label: `${i.guildsName} (${i.user?.mobile}) / نوع فعالیت: ${i.typeActivity} / حوزه فعالیت: ${i.areaActivity}`,
item: i,
}))
: []
}
value={formik.values.guild}
onChange={(e, item) => {
formik.setFieldValue("steward_key", item?.id);
formik.validateForm();
}}
error={formik.touched.guild && Boolean(formik.errors.guild)}
helperText={formik.touched.guild && formik.errors.guild}
getOptionLabel={(option) => option.label}
renderInput={(params) => (
<TextField
{...params}
label={`انتخاب ${userType === "steward" ? "مباشر" : "صنف"}`}
variant="outlined"
/>
)}
/>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
id="allocation_limit"
label="وزن تعهد روزانه"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.allocation_limit}
error={
formik.touched.allocation_limit
? Boolean(formik.errors.allocation_limit)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.allocation_limit &&
Boolean(formik.errors.allocation_limit)
? formik.errors.allocation_limit
: null
}
/>
</Grid>
<Grid>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChangeRadioButton}
row
sx={{ alignItems: "center" }}
>
<FormLabel id="demo-radio-buttons-group-label">
نوع تعهد: {" "}
</FormLabel>
<FormControlLabel
value="forced"
control={<Radio />}
label="اجباری"
/>
<FormControlLabel
value="optional"
control={<Radio />}
label="اختیاری"
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid container xs={12}>
<Button
fullWidth
variant="contained"
disabled={!formik.isValid}
onClick={() => {
dispatch(
provinceDispensersAddSteward({
guilds_key: formik.values.steward_key,
choose: true,
type: type,
owner_key: killHousekey,
allocation_limit: formik.values.allocation_limit,
allocation_type: value,
choose_type: userType,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,102 @@
import React, { useContext } from "react";
import { TextField, Button, Grid } from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { AppContext } from "../../../../contexts/AppContext";
import { useDispatch } from "react-redux";
import {
provinceGetAllocatedRequestsService,
provinceUpdateAllocatedRequestService,
} from "../../services/province-get-allocated-requests";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const ProvinceAllocatedRequestsEditAllocation = ({
selectedDate1,
selectedDate2,
item,
fetchDashboard,
}) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
amount: item.allocatedQuantity ? item.allocatedQuantity : "",
},
validationSchema: Yup.object({
amount: Yup.number()
.required("وارد کردن حجم الزامی است")
.min(1, "حجم باید بزرگتر از صفر باشد"),
}),
onSubmit: (values) => {
dispatch(
provinceUpdateAllocatedRequestService({
edit_allocation_quantity: true,
province_kill_request_key: item?.provinceKillRequestKey,
quantity: parseInt(values?.amount),
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
provinceGetAllocatedRequestsService({
selectedDate1,
selectedDate2,
})
);
fetchDashboard();
dispatch(CLOSE_MODAL());
}
});
},
});
return (
<Grid
container
spacing={2}
direction="column"
alignItems="center"
justifyContent="center"
>
<Grid item xs={12}>
<form onSubmit={formik.handleSubmit}>
<TextField
fullWidth
id="amount"
name="amount"
label="حجم"
value={formik.values.amount}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.amount && Boolean(formik.errors.amount)}
helperText={formik.touched.amount && formik.errors.amount}
variant="outlined"
/>
<Button
fullWidth
type="submit"
variant="contained"
color="primary"
style={{ marginTop: "16px" }}
>
ارسال
</Button>
</form>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,64 @@
import { IconButton, Tooltip } from "@mui/material";
import React, { useState, useRef } from "react";
import { BsFillFileEarmarkPdfFill } from "react-icons/bs";
import { useReactToPrint } from "react-to-print";
import ProvinceAllcationInvoice from "../province-allocation-invoice/ProvinceAllcationInvoice";
import { useDispatch } from "react-redux";
import { provinceGetAllocationLetter } from "../../services/province-get-allocation-letter";
import { useEffect } from "react";
import { PropTypes } from "prop-types";
export const ProvinceAllocatedRequestsInvoiceOperation = ({ date1, date2 }) => {
const dispatch = useDispatch();
const [factorData, setFactorData] = useState(null);
const componentRef = useRef();
const handleDownloadFactor = () => {
const result = dispatch(provinceGetAllocationLetter(date1));
setFactorData(result);
};
const printPDF = useReactToPrint({
content: () => componentRef.current,
documentTitle: "گزارش روزانه",
onAfterPrint: () => {
setFactorData(null);
},
});
useEffect(() => {
if (factorData) {
printPDF();
}
}, [factorData, printPDF]);
return (
<div>
<Tooltip title="خروجی PDF">
<IconButton
disabled={date2 && date1 !== date2}
onClick={() => {
handleDownloadFactor();
}}
size={"large"}
aria-label="delete"
color="success"
>
<BsFillFileEarmarkPdfFill size={32} />
</IconButton>
</Tooltip>
<div style={{ visibility: "hidden", position: "absolute" }}>
<ProvinceAllcationInvoice
ref={componentRef}
item={factorData?.payload?.data}
date={date1}
/>
</div>
</div>
);
};
ProvinceAllocatedRequestsInvoiceOperation.propTypes = {
date1: PropTypes.any,
date2: PropTypes.any,
};

View File

@@ -0,0 +1,218 @@
import {
Button,
IconButton,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import { useContext, useState } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useDispatch } from "react-redux";
import { provinceAllocatedDeleteService } from "../../services/province-allocated-delete";
import { AppContext } from "../../../../contexts/AppContext";
import { provinceGetAllocatedRequestsService } from "../../services/province-get-allocated-requests";
import { provinceAllocatedReturnQuantityService } from "../../services/province-allocated-return-quantity";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { ProvinceAllocatedRequestsEditAllocation } from "../province-allocated-requests-edit-allocation/ProvinceAllocatedRequestsEditAllocation";
import KeyboardReturnIcon from "@mui/icons-material/KeyboardReturn";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
export const ProvinceAllocatedRequestsOperations = ({
item,
fetchDashboard,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif, , selectedDate1, , selectedDate2] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
const btnDisabled =
item.allocatedState === "rejected" ||
(item.allocatedCarState && item.allocatedRemainQuantity === 0);
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
disabled={btnDisabled}
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<div style={{ padding: 10 }}>
<Grid
container
direction="column"
gap={SPACING.TINY}
alignItems="flex-start"
style={{ width: 180 }}
>
{getRoleFromUrl() !== "KillHouse" && (
<Tooltip placement="left" title="ویرایش">
<Button
size="small"
disabled={item?.directBuying}
startIcon={<EditIcon fontSize="small" />}
sx={{
"&.Mui-disabled": {
backgroundColor: "transparent",
color: "text.disabled",
},
}}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص",
content: (
<ProvinceAllocatedRequestsEditAllocation
item={item}
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
fetchDashboard={fetchDashboard}
/>
),
})
);
}}
>
<Typography variant="body2" textAlign="left" fontWeight={600}>
ویرایش تخصیص
</Typography>
</Button>
</Tooltip>
)}
{getRoleFromUrl() !== "KillHouse" && (
<Tooltip placement="left" title="تخصیص مانده برگشت">
<Button
size="small"
startIcon={<KeyboardReturnIcon fontSize="small" />}
sx={{
"&.Mui-disabled": {
backgroundColor: "transparent",
color: "text.disabled",
},
}}
onClick={() => {
dispatch(
provinceAllocatedReturnQuantityService({
province_kill_request_key: item.provinceKillRequestKey,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
handleClose();
dispatch(
provinceGetAllocatedRequestsService({
selectedDate1,
selectedDate2,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<Typography variant="body2" textAlign="left" fontWeight={600}>
تخصیص مانده برگشت
</Typography>
</Button>
</Tooltip>
)}
<Tooltip placement="left" title="حذف">
<Button
size="small"
disabled={
getRoleFromUrl() !== "AdminX" && item.allocatedCarState
}
color="error"
startIcon={<DeleteIcon fontSize="small" />}
sx={{
"&.Mui-disabled": {
backgroundColor: "transparent",
color: "text.disabled",
},
}}
onClick={() => {
dispatch(
provinceAllocatedDeleteService({
province_kill_request_key: item.provinceKillRequestKey,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
handleClose();
dispatch(
provinceGetAllocatedRequestsService({
selectedDate1,
selectedDate2,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<Typography variant="body2" textAlign="left" fontWeight={600}>
حذف
</Typography>
</Button>
</Tooltip>
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,400 @@
import {
Button,
Checkbox,
FormControlLabel,
Tab,
Tabs,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import axios from "axios";
import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import { provinceGetAllocatedRequestsService } from "../../services/province-get-allocated-requests";
import { ProvinceAllocatedRequestsOperations } from "../province-allocated-requests-operations/ProvinceAllocatedRequestsOperations";
import { ProvinceGetDeletedAllocatedRequests } from "../province-get-deleted-allocated-requests/ProvinceGetDeletedAllocatedRequests";
import { provinceGetDashboardKillRequestService } from "../../services/get-dahsnoard-province-kill-request";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const ProvinceAllocatedRequests = () => {
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState();
const [hasDocumentState, setHasDocumentState] = useState(false);
const [selectedTab, setSelectedTab] = useState(0);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [textValue, setTextValue] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleChangeDocumentState = (event) => {
const checked = event.target.checked;
setHasDocumentState(checked);
dispatch(
provinceGetAllocatedRequestsService({
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
selectedDate1,
selectedDate2,
textValue,
hasDocumentState: checked,
})
);
fetchDashboard(checked);
};
const handleTabChange = (event, newValue) => {
setSelectedTab(newValue);
};
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const { provinceGetAllocatedRequests } = useSelector(
(state) => state.provinceSlice
);
const [dashboardData, setDashboardData] = useState([]);
const fetchDashboard = (checked = hasDocumentState) => {
const params = {
selectedDate1,
selectedDate2,
textValue,
hasDocumentState: checked,
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
};
dispatch(provinceGetDashboardKillRequestService(params)).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
if (selectedTab === 0) {
dispatch(
provinceGetAllocatedRequestsService({
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
selectedDate1,
selectedDate2,
textValue,
hasDocumentState,
})
);
fetchDashboard();
}
}, [selectedDate1, selectedDate2, selectedTab, selectedSubUser?.key]);
const handleSubmit = () => {
if (selectedTab === 0) {
dispatch(
provinceGetAllocatedRequestsService({
role_key: checkPathStartsWith("province")
? selectedSubUser?.key || ""
: "",
selectedDate1,
selectedDate2,
textValue,
hasDocumentState,
})
);
fetchDashboard(hasDocumentState);
}
};
useEffect(() => {
const sortedRequests = provinceGetAllocatedRequests
?.slice()
.sort((a, b) => b.allocatedRemainQuantity - a.allocatedRemainQuantity)
.map((item, i) => {
let state = "";
if (item.allocatedState === "pending") {
state = "در انتظار تایید";
} else if (item.allocatedState === "accepted") {
state = "تایید شده";
} else if (item.allocatedState === "rejected") {
state = "رد شده";
}
let sellType = "";
if (item?.market) {
sellType = "پنل معاملات";
} else if (item?.directBuying) {
sellType = "خرید مستقیم";
} else if (item?.union) {
sellType = "خرید خارج از استان";
} else {
sellType = "اتحادیه";
}
return [
i + 1,
item?.orderCode,
sellType,
item?.freeSaleInProvince === false ? "دولتی" : "آزاد",
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.poultryUnitName,
`${item?.poultryFullName} (${item?.poultryMobile})`,
item?.poultryCity,
formatJustDate(item?.sendDate),
item?.poultryQuantity?.toLocaleString(),
formatTime(item?.dateOfAllocate),
item?.killHouseName,
item?.killHouseCity,
item?.allocatedQuantity?.toLocaleString(),
item?.indexWeight?.toLocaleString(),
item?.totalWeight?.toLocaleString(),
item?.amount?.toLocaleString() + " ﷼",
item?.killHousePrice?.toLocaleString() + " ﷼",
state,
item?.allocatedCarState ? "دارد" : "ندارد",
item?.allocatedRemainQuantity?.toLocaleString(),
item?.returnToProvince === false ? (
<ProvinceAllocatedRequestsOperations
item={item}
key={i}
fetchDashboard={fetchDashboard}
/>
) : (
<Tooltip
disableHoverListener={!item?.returner}
key={i}
title={
<>
{item?.returner && (
<Grid
container
direction="column"
justifyContent="center"
xs={12}
>
<Typography variant="body2">
{item?.returner?.fullname || "-"} (
{item?.returner?.mobile || "-"})
</Typography>
<Typography variant="body2">
{item?.returner?.date
? formatJustDate(item?.returner?.date)
: "-"}
</Typography>
</Grid>
)}
</>
}
>
<Typography
variant="body2"
sx={{
cursor: item?.returner ? "pointer" : "default",
}}
>
تخصیص برگشت داده شده
</Typography>
</Tooltip>
),
];
});
setDataTable(sortedRequests);
}, [provinceGetAllocatedRequests]);
return (
<Grid>
<Grid
container
justifyContent="center"
alignItems="center"
mb={SPACING.MEDIUM}
mt={SPACING.SMALL}
>
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={selectedTab}
onChange={handleTabChange}
>
<Tab label="تخصیص ها" />
<Tab label="تخصیص های حذف شده" />
</Tabs>
</Grid>
{selectedTab === 0 && (
<Grid container alignItems="center" justifyContent="center">
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid style={{ width: 160 }}>
<DatePicker
label="از تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid style={{ width: 160 }}>
<DatePicker
label="تا تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}allocated_excel/?start=${selectedDate1}&end=${selectedDate2}&role=${getRoleFromUrl()}${
checkPathStartsWith("province")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}&filter=search&value=${textValue}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
<FormControlLabel
control={
<Checkbox
checked={hasDocumentState}
onChange={handleChangeDocumentState}
name="hasDocumentState"
/>
}
label="نمایش تخصیصات بدون بار"
/>
</Grid>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد تخصیصات",
"حجم تخصیصات",
"وزن تخصیصات",
"تعداد تخصیصات تایید شده",
"حجم تخصیصات تایید شده",
"تعداد تخصیصات در انتظار تایید",
"حجم تخصیصات در انتظار تایید",
"تعداد تخصیصات رد شده",
"حجم تخصیصات رد شده",
"تعداد تخصیصات دارای ماشین",
"حجم تخصیصات دارای ماشین",
"وزن تخصیصات دارای ماشین",
"تعداد بارها",
"حجم بارها",
"وزن بارها",
"تعداد تخصیصات فاقد ماشین",
"حجم تخصیصات فاقد ماشین",
"وزن تخصیصات فاقد ماشین",
]}
data={[
[
dashboardData?.lenProvinceRequest?.toLocaleString(),
dashboardData?.quantity?.toLocaleString(),
dashboardData?.weight?.toLocaleString(),
dashboardData?.acceptedProvinceKillRequests?.toLocaleString(),
dashboardData?.acceptedProvinceKillRequestsQuantity?.toLocaleString(),
dashboardData?.pendingProvinceKillRequests?.toLocaleString(),
dashboardData?.pendingProvinceKillRequestsQuantity?.toLocaleString(),
dashboardData?.rejectedProvinceKillRequests?.toLocaleString(),
dashboardData?.rejectedProvinceKillRequestsQuantity?.toLocaleString(),
dashboardData?.lenHasCar?.toLocaleString(),
dashboardData?.hasCarQuantity?.toLocaleString(),
dashboardData?.hasCarWeight?.toLocaleString(),
dashboardData?.lenKillRequests?.toLocaleString(),
dashboardData?.realQuantity?.toLocaleString(),
dashboardData?.realWeight?.toLocaleString(),
dashboardData?.lenHasntCar?.toLocaleString(),
dashboardData?.hasntCarQuantity?.toLocaleString(),
dashboardData?.hasntCarWeight?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<ResponsiveTable
title="تخصیص های انجام شده"
paginated
noSearch
columns={[
"ردیف",
"کدسفارش",
"درخواست",
"فروش",
"کشتار",
"نام فارم",
"نام مرغدار",
"شهر",
"تاریخ کشتار",
"تعداد کشتار",
"تاریخ تخصیص",
"خریدار",
"شهر خریدار",
"تعداد تخصیص",
"میانگین وزنی",
"وزن تقریبی",
"قیمت مرغدار",
"قیمت کشتارگاه",
"وضعیت تایید خریدار",
"وضعیت تخصیص ماشین",
"مانده قابل تخصیص",
"عملیات",
]}
data={dataTable}
/>
</Grid>
)}
{selectedTab === 1 && <ProvinceGetDeletedAllocatedRequests />}
</Grid>
);
};

View File

@@ -0,0 +1,406 @@
import React, { forwardRef } from "react";
import logo from "../../../../assets/images/ChickenLogo.png";
import { PropTypes } from "prop-types";
import { formatJustDate } from "../../../../utils/formatTime";
import { getCeoName } from "../../../../utils/getCeoName";
import { useSystemName } from "../../../../utils/getSystemName";
const styles = {
page: {
width: "210mm",
margin: "0 auto",
display: "flex",
flexDirection: "column",
position: "relative",
direction: "rtl",
fontFamily: "nazanin",
fontWeight: "bold",
},
container: {
width: "95%",
alignSelf: "center",
pageBreakInside: "avoid",
},
p: {
fontFamily: "nazanin",
fontWeight: "bold",
pAlign: "justify",
},
span: {
fontFamily: "nazanin",
fontWeight: "bold",
pAlign: "justify",
},
invoiceTable: {
width: "100%",
borderCollapse: "collapse",
alignSelf: "center",
},
tableCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 11,
},
tableCellMobile: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 10,
},
tableInnerCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 8,
whiteSpace: "nowrap",
},
tableHeader: {
backgroundColor: "rgba(255, 229, 153, 0.5)",
pageBreakAfter: "auto",
},
headerRow: {
backgroundColor: "rgba(255, 229, 153, 0.6)",
color: "black",
pageBreakInside: "avoid",
pageBreakAfter: "auto",
},
logo: {
width: "60px",
height: "auto",
marginBottom: "10px",
},
contentContainer: {
display: "flex",
justifyContent: "space-between",
marginTop: "20px",
marginLeft: "100px",
marginRight: "30px",
},
contentInLine: {
display: "flex",
flexDirection: "column",
alignItems: "center",
},
mainTitle: {
fontFamily: "nazanin",
fontSize: 11,
pAlign: "center",
fontWeight: "bolder",
},
signature: {
display: "flex",
flexDirection: "column",
alignItems: "flex-end",
marginLeft: "20px",
},
watermarkContainer: {
position: "fixed",
top: 450,
left: 0,
right: 30,
bottom: 0,
justifyContent: "center",
alignItems: "center",
opacity: 0.2,
zIndex: -1,
},
watermarkp: {
fontSize: 100,
fontWeight: "bolder",
color: "grey",
transform: "rotate(-45deg)",
left: "50%",
},
title: {
fontSize: 12,
fontWeight: "bolder",
pAlign: "center",
},
titleTopic: {
marginTop: "10px",
fontSize: 12,
fontWeight: "bolder",
pAlign: "center",
},
firsttitle: {
fontSize: 14,
fontWeight: "bolder",
marginLeft: "40px",
pAlign: "center",
},
title2: {
fontSize: 10,
marginBottom: 10,
pAlign: "center",
},
options: {
marginLeft: "50px",
padding: "10px",
marginTop: "15px",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
},
divider: {
width: "100%",
height: "2px",
backgroundColor: "red",
marginBottom: 15,
},
pTitleContainer: {
pAlign: "right",
margin: "15px",
textAlign: "justify",
textJustify: "inter-word",
},
tableHeaderCell: {
backgroundColor: "rgba(255, 229, 153, 0.5)",
fontSize: 10,
border: "1px solid #000",
padding: "4px",
textAlign: "center",
fontWeight: "bold",
},
footer: {
pageBreakAfter: "always",
position: "fixed",
left: 0,
bottom: 0,
width: "100%",
},
};
const ProvinceAllcationInvoice = forwardRef((props, ref) => {
const { item } = props;
const { date } = props;
const totalCapacity = item?.reduce(
(total, currentItem) => total + (currentItem?.quantity || 0),
0
);
function getItemInfoQuantity(item) {
let totalQuantity = 0;
item?.forEach((option) => {
if (option?.provinceKillRequests) {
option.provinceKillRequests.forEach((request) => {
if (request.info?.quantity !== undefined) {
totalQuantity += request.info.quantity;
}
});
}
});
return totalQuantity;
}
function getItemInfoWeight(item) {
let totalQuantity = 0;
item?.forEach((option) => {
if (option?.provinceKillRequests) {
option.provinceKillRequests.forEach((request) => {
if (request.info?.weight !== undefined) {
totalQuantity += request.info.weight;
}
});
}
});
return totalQuantity;
}
// function getAddressContent(systemName) {
// switch (systemName) {
// case "استان اردبیل":
// return "آدرس : اردبیل، شهرک کارشناسان ،جنب ساختمان نظام مهندسی، ساختمان فرهنگ، طبقه دوم تلفن : 33749254 تلفاکس : 33749253 ";
// case "استان همدان":
// return "همدان، بلوار آیت اله مدنی، کوچه امامزاده یحیی یک تلفن: 081 32523689 ";
// case "استان آذربایجان شرقی":
// return "تبریز خیابان راه آهن نبش کوی اشکان ساختمان ۱۴ طبقه دوم تلفن: 041 34502363";
// default:
// return "خرم آباد، مطهری، شرکت مهندسی نرم افزار آرتا مهر آرتان تلفن: 09011110919";
// }
// }
const systemName = useSystemName();
return (
<div style={styles.page} ref={ref}>
<div style={styles.contentContainer}>
<div style={styles.contentInLine}>
<img alt="logo" src={logo} style={styles.logo} />
<span style={styles.mainTitle}>
اتحادیه سراسری تعاونیهای کشاورزی پرورش دهندگان مرغ گوشتی ایران
</span>
<span style={styles.title}>
اتحادیه شرکت های تعاونی کشاورزی مرغداران {" "} {systemName}
</span>
</div>
<div>
<span style={styles.firsttitle}>بسمه تعالی</span>
</div>
<div style={styles.options}>
<span style={styles.title}>شماره:</span>
<span style={styles.titleTopic}>تاریخ:</span>
<span style={styles.titleTopic}>پیوست:</span>
</div>
</div>
<hr style={styles.divider} />
<p style={styles.pTitleContainer}>
<strong style={{ fontSize: "20px" }}>
معاونت محترم توسعه بازرگانی و صنایع کشاورزی
<br />
با سلام
<br />
</strong>
<br />
احتراماً گزارش تخصیص و کشتار مورخ {" "}
{formatJustDate(date)}، مرغ گوشتی استان جهت استحضار بحضورتان ارسال می
گردد.
</p>
<div style={styles.container}>
<p style={{ marginTop: "40px" }}>
اطلاعات تخصیص:{" "}
<span style={{ color: "green", fontWeight: "boler" }}>
{" "}
تعداد درخواست کشتار (
{totalCapacity !== undefined && totalCapacity.toLocaleString()}{" "}
قطعه)، تخصیص داده شده ({getItemInfoQuantity(item).toLocaleString()}{" "}
قطعه)، وزن کل تخصیص ({" "}
{Math.round(getItemInfoWeight(item)).toLocaleString()} کیلوگرم).
</span>
</p>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>مرغدار</th>
<th style={styles.tableHeaderCell}>تلفن</th>
<th style={styles.tableHeaderCell}>شهر</th>
<th style={styles.tableHeaderCell}>تعداد</th>
<th style={styles.tableHeaderCell}>میانگین وزنی</th>
<th style={styles.tableHeaderCell}>سن</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>تعداد</th>
<th style={styles.tableHeaderCell}>وزن تقریبی بار</th>
<th style={styles.tableHeaderCell}>محل کشتار</th>
</tr>
</thead>
<tbody>
{item?.map((item, i) => (
<tr key={i}>
<td style={styles.tableCell}>{i + 1}</td>
<td style={styles.tableCell}>
{item?.poultry?.user?.fullname}
</td>
<td style={styles.tableCellMobile}>
{item?.poultry?.user?.mobile}
</td>
<td style={styles.tableCell}>
{item?.poultry?.address?.city?.name}
</td>
<td style={styles.tableCell}>
{item?.quantity.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.IndexWeight.toLocaleString()}
</td>
<td style={styles.tableCell}>{item?.hatching?.age}</td>
<td style={styles.tableInnerCell}>
{item?.provinceKillRequests?.map((item, i, array) => (
<>
{item?.killhouseUser?.killHouseOperator?.user?.fullname
? item?.killhouseUser?.killHouseOperator?.user?.fullname
: "............"}
<br />
{i < array.length - 1 && (
<div style={{ borderBottom: "1px solid #000" }} />
)}
</>
))}
</td>
<td style={styles.tableInnerCell}>
{item?.provinceKillRequests?.map((item, i, array) => (
<>
{item?.info?.quantity
? item?.info?.quantity.toLocaleString()
: "............"}
<br />
{i < array.length - 1 && (
<div style={{ borderBottom: "1px solid #000" }} />
)}
</>
))}
</td>
<td style={styles.tableInnerCell}>
{item?.provinceKillRequests?.map((item, i, array) => (
<>
{Math.round(item?.info?.weight)
? Math.round(item?.info?.weight).toLocaleString()
: "............"}
<br />
{i < array.length - 1 && (
<div style={{ borderBottom: "1px solid #000" }} />
)}
</>
))}
</td>
<td style={styles.tableInnerCell}>
{item?.provinceKillRequests?.map((item, i, array) => (
<>
{item?.info?.killPlace
? item?.info?.killPlace
: "............"}
<br />
{i < array.length - 1 && (
<div style={{ borderBottom: "1px solid #000" }} />
)}
</>
))}
</td>
</tr>
))}
</tbody>
</table>
<div style={{ flexDirection: "row", marginTop: "20px" }}>
<div style={styles.signature}>
<div style={styles.contentInLine}>
<span style={styles.title}>{getCeoName()}</span>
<span style={styles.title}>
مدیرعامل اتحادیه مرغداران{" "}
{systemName}
</span>
</div>
</div>
</div>
<div style={styles.watermarkContainer}>
<p style={styles.watermarkp}>سامانه رصدیار </p>
</div>
{/* <div style={styles.footer}>
<div style={styles.divider} />
<p style={{ fontSize: 12, padding: "10px", marginRight: "10px" }}>
{getAddressContent(systemName)}
</p>
</div> */}
</div>
</div>
);
});
ProvinceAllcationInvoice.displayName = "ProvinceAllcationInvoice";
export default ProvinceAllcationInvoice;
ProvinceAllcationInvoice.propTypes = {
item: PropTypes.any,
};

View File

@@ -0,0 +1,86 @@
import { Button, Checkbox, FormControlLabel, TextField } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { getLossesPermissionService } from "../../services/get-losses-permission";
import { updateLossesPermissionService } from "../../services/update-losses-permission";
export const ProvinceApplyDefaultLosses = () => {
const [isChecked, setIsChecked] = useState(true);
const [numberValue, setNumberValue] = useState("");
const [data, setData] = useState();
const dispatch = useDispatch();
useEffect(() => {
dispatch(getLossesPermissionService()).then((r) => {
setIsChecked(r.payload.data.allow);
setNumberValue(r.payload.data.percent);
setData(r.payload.data);
});
}, []);
useEffect(() => {
if (!isChecked) {
dispatch(
updateLossesPermissionService({
permission_key: data.key,
allow: false,
percent: parseInt(numberValue),
})
);
}
}, [isChecked]);
const handleChange = (event) => {
setIsChecked(event.target.checked);
};
// Define a handleChange function to update the state when the text field value changes
const handleChangeNumber = (event) => {
// Ensure that the entered value is a number (or an empty string)
const newValue = event.target.value;
if (/^\d*$/.test(newValue) || newValue === "") {
setNumberValue(newValue);
}
};
return (
<Grid container alignItems="center" gap={SPACING.TINY}>
<FormControlLabel
control={
<Checkbox checked={isChecked} onChange={handleChange} name="item4" />
}
label="اعمال درصد تلفات پیش فرض:"
/>
<Grid>
<TextField
disabled={!isChecked}
type="number"
label="درصد تلفات"
size="small"
value={numberValue}
onChange={handleChangeNumber}
inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }}
/>
</Grid>
<Grid>
<Button
variant="contained"
disabled={!isChecked || !numberValue}
onClick={() => {
dispatch(
updateLossesPermissionService({
permission_key: data.key,
percent: parseInt(numberValue),
allow: isChecked,
})
);
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,25 @@
import { Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { formatTime } from "../../../../utils/formatTime";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
export const ProvinceArchiveFeeDetails = ({ item }) => {
return (
<Grid container width="100%">
<Grid container width="100%" justifyContent="space-between">
<Typography>تاریخ و زمان:</Typography>
<Typography>{formatTime(item?.archiver?.date)}</Typography>
</Grid>
<Grid container width="100%" justifyContent="space-between">
<Typography>بایگانی کننده:</Typography>
<Typography>
{item?.archiver?.fullname} ({getFaUserRole(item.archiver.role)})
</Typography>
</Grid>
<Grid container width="100%" justifyContent="space-between">
<Typography>دلیل بایگانی:</Typography>
<Typography>{item?.archiveMessage}</Typography>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,76 @@
import { Button, TextField } from "@mui/material";
import { Box } from "@mui/system";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceArchiveFeeOperationService } from "../../services/province-archive-fee-operation";
export const ProvinceArchiveFeeOperation = ({ selectedItems, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [text, setText] = useState("");
const handleChange = (event) => {
setText(event.target.value);
};
const submitForm = () => {
dispatch(
provinceArchiveFeeOperationService({
role: getRoleFromUrl(),
message: text,
province_kill_request_list: selectedItems,
type: "archive",
})
).then((r) => {
updateTable();
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
};
return (
<Grid width="100%">
<Box>
<TextField
id="outlined-multiline-static"
label="دلیل بایگانی"
multiline
rows={3}
variant="outlined"
fullWidth
value={text}
onChange={handleChange}
/>
</Box>
<Box mt={1}>
<Button
variant="contained"
color="primary"
onClick={submitForm}
disabled={!text}
fullWidth
>
ثبت
</Button>
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,365 @@
import {
Button,
Checkbox,
FormControlLabel,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import axios from "axios";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { Grid } from "../../../../components/grid/Grid";
import { PageTable } from "../../../../components/page-table/PageTable";
import { SPACING } from "../../../../data/spacing";
import { ROUTE_PROVINCE_PAYING_FEES_REQUESTS } from "../../../../routes/routes";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const ProvinceArchiveFees = () => {
const userInfo = useSelector((state) => state.userSlice);
const navigate = useNavigate();
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [withDate, setWithDate] = useState(false);
const [textValue, setTextValue] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const fetchApiData = async (page, textValue) => {
setLoading(true);
const response = await axios.get(
`kill_house_total_wage/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&type=archive&page=${page}&page_size=${perPage}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
const handlePerRowsChange = async (newPerPage, page) => {
setLoading(true);
const response = await axios.get(
`kill_house_total_wage/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&type=archive&page=${page}&page_size=${newPerPage}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
};
useEffect(() => {
fetchApiData(1);
}, []);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`kill_house_total_wage/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&type=archive${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, withDate]);
const columns = [
{
name: "خریدار",
selector: (item) => {
const type = item?.info?.killer ? "کشتارکن" : "کشتارگاه";
return `${type} ${item?.info?.killHouseName} - ${item?.info?.killHouseFullname} (${item?.info?.killHouseMobile})`;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "شهر",
selector: (item) => `${item?.info?.killHouseCity}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
// {
// name: "تعداد کل سفارشات",
// selector: (item) => item?.info?.totalCount,
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// // width: "80px",
// },
// {
// name: "تعداد کل تخصیص (قطعه)",
// selector: (item) => item?.info?.totalQuantity?.toLocaleString(),
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// },
// {
// name: "وزن کل (کیلوگرم)",
// selector: (item) => item?.info?.totalWeight,
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// // width: "80px",
// },
// {
// name: "پرداخت شده (سفارش)",
// selector: (item) => item.info?.paidCount,
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// // width: "90px",
// },
{
name: "تعداد پرونده",
selector: (item) => item?.info?.archiveCount?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "حجم",
selector: (item) => item.info?.totalArchiveQuantity?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "وزن سفارشات (کیلوگرم)",
selector: (item) => {
return item.info?.totalWeightArchive?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
// {
// name: "مبلغ کل تعرفه (ریال)",
// selector: (item) => item?.info?.totalWage?.toLocaleString(),
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// // width: "90px",
// },
// {
// name: "مبلغ تسویه شده (تعرفه )",
// selector: (item) => item?.info?.totalPaidWage?.toLocaleString(),
// sortable: true,
// wrap: true,
// allowOverflow: true,
// center: true,
// // width: "90px",
// },
{
name: "مبلغ تعرفه (﷼)",
selector: (item) => item?.info?.totalArchiveWage?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "جزییات سفارش",
selector: (item) => {
return (
<Button
onClick={() => {
navigate(
`${ROUTE_PROVINCE_PAYING_FEES_REQUESTS}/archive/${item.key}`
);
}}
>
مشاهده
</Button>
);
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
];
return (
<Grid container justifyContent="end">
<PageTable
title={
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>تعرفه های معوقه</Typography>
<Grid
container
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "10px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
>
<Grid>
<FormControlLabel
control={
<Checkbox
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
/>
}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}kill_house_total_wage_archive_excel/?key=${
userInfo?.userProfile?.key
}${textValue}${
withDate
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ``
}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
</Grid>
);
};

View File

@@ -0,0 +1,180 @@
import { Button, IconButton } from "@mui/material";
import PrintIcon from "@mui/icons-material/Print";
import { Grid } from "../../../../components/grid/Grid";
import { formatJustDate } from "../../../../utils/formatTime";
import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight";
import { SPACING } from "../../../../data/spacing";
export const ProvinceAutoAllocatedView = ({
dataTable,
item,
setIsSingleView,
}) => {
const sumOfAllocationQuantity =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return (
total +
item.allocations?.reduce((allocationTotal, allocation) => {
return allocationTotal + allocation.allocationQuantity;
}, 0)
);
}, 0);
const sumAllocationsSum =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return total + item.allocationsSum;
}, 0);
const sumOfKillRequests =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return total + item.poultryQuantity;
}, 0);
return (
<Grid container alignItems="start" my={SPACING.SMALL}>
<table className="auto-allocation-table">
<tr>
<th>
<Grid>
<Button
color="error"
onClick={() => {
setIsSingleView(null);
}}
>
<KeyboardArrowRightIcon />
بازگشت به بایگانی
</Button>
</Grid>
</th>
<th>کد تخصیص خودکار {item.allocationOrderCode}</th>
<th>تاریخ تخصیص {formatJustDate(item.allocationDate)}</th>
</tr>
</table>
<table className="auto-allocation-table">
<tr>
<th>ردیف</th>
<th>نام واحد (مرغدار)</th>
<th>تلفن مرغدار</th>
<th>آدرس</th>
<th>تعداد درخواست کشتار</th>
<th>مانده قابل تخصیص</th>
<th>ماهیت خریدار</th>
<th>نام خریدار</th>
<th>تلفن خریدار</th>
<th>آدرس</th>
<th>محل کشتار</th>
<th>تعداد تخصیص</th>
<th>جمع مرغ تخصیصی</th>
<th>سهم درصد</th>
<th>حواله مرغدار</th>
<th>حواله خریدار</th>
</tr>
{dataTable?.map((poultryRequestItem, j) => {
// const sumOfAllocation = poultryRequestItem.allocations?.reduce(
// (total, item) => {
// return total + item.allocationQuantity;
// },
// 0
// );
return poultryRequestItem?.allocations?.map((slaughter, i) => {
const reaminForAllocation =
poultryRequestItem?.poultryQuantity -
poultryRequestItem?.allocationsSum;
// if (reaminForAllocation < 0) {
// reaminForAllocation = (
// <Typography color={"red"}>{reaminForAllocation}</Typography>
// );
// }
return (
<tr key={i}>
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{j + 1}
</td>
)}
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{poultryRequestItem?.poultryName}
</td>
)}
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{poultryRequestItem?.poultryMobile}
</td>
)}
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{poultryRequestItem?.poultryAddress}
</td>
)}
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{poultryRequestItem?.poultryQuantity?.toLocaleString()}
</td>
)}
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{/* {poultryRequestItem?.poultryRemainQuantity} */}
{reaminForAllocation.toLocaleString()}
</td>
)}
<td>{slaughter?.type ? "کشتارکن" : "کشتارگاه"}</td>
<td>{slaughter?.buyerName}</td>
<td>{slaughter?.buyerMobile}</td>
<td>{slaughter?.buyerAddress}</td>
<td>{slaughter?.KillingPlace}</td>
<td>{slaughter?.allocationQuantity}</td>
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
{poultryRequestItem?.allocationsSum?.toLocaleString()}
{/* {sumOfAllocation} */}
</td>
)}
<td>
%
{(
(slaughter?.allocationQuantity / sumOfAllocationQuantity) *
100
).toFixed(2)}
</td>
{!i && (
<td rowSpan={poultryRequestItem?.allocations.length}>
<IconButton disabled={true} color="primary">
<PrintIcon />
</IconButton>
</td>
)}
<td>
<IconButton disabled={true} color="primary">
<PrintIcon />
</IconButton>
</td>
</tr>
);
});
})}
<tr>
<td colSpan={"11"}>جمع کل</td>
<td>{sumOfAllocationQuantity.toLocaleString()}</td>
<td>{sumAllocationsSum.toLocaleString()}</td>
<td>
{((sumOfAllocationQuantity / sumOfKillRequests) * 100).toFixed(2)}
</td>
</tr>
</table>
</Grid>
);
};

View File

@@ -0,0 +1,130 @@
import { IconButton, TextField, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { provinceGetArchiveAutoAllocationsService } from "../../services/province-get-archive-auto-allocations";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { ProvinceAutoAllocatedView } from "../province-auto-allocated-view/ProvinceAutoAllocatedView";
import { formatJustDate } from "../../../../utils/formatTime";
export const ProvinceAutoAllocationArchive = () => {
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState([]);
const [isSingleView, setIsSingleView] = useState();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const { provinceGetArchiveAutoAllocations } = useSelector(
(state) => state.provinceSlice
);
useEffect(() => {
dispatch(
provinceGetArchiveAutoAllocationsService({ selectedDate1, selectedDate2 })
);
}, [selectedDate1, selectedDate2]);
useEffect(() => {
const d = provinceGetArchiveAutoAllocations?.map((item, i) => {
return [
i + 1,
item.allocationOrderCode,
formatJustDate(item.allocationDate),
item.totalQuantity.toLocaleString(),
item.allocatedQuantity.toLocaleString(),
item.numberOfPoultry.toLocaleString(),
item.numberOfKillHouse.toLocaleString(),
<IconButton
color="primary"
key={i}
onClick={() => {
setIsSingleView(item);
}}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [provinceGetArchiveAutoAllocations]);
return (
<Grid mt={SPACING.SMALL} xs={12} px={2}>
{!isSingleView && (
<>
<Grid container alignItems="center" gap={SPACING.SMALL} mb={2}>
<Grid>
<Typography>بایگانی تخصیصات خودکار</Typography>
</Grid>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<ResponsiveTable
title="بایگانی تخصیصات خودکار"
columns={[
"ردیف",
"کد تخصیص خودکار",
"تاریخ تخصیص",
"تعداد درخواست کشتار",
"تعداد تخصیصی",
"تعداد مرغدار",
"تعداد کشتارگاه",
"مشاهده",
]}
data={dataTable}
/>
</>
)}
{isSingleView && (
<Grid
container
direction="column"
alignItems="start"
gap={SPACING.SMALL}
>
<ProvinceAutoAllocatedView
dataTable={isSingleView.allocationsList}
item={isSingleView}
setIsSingleView={setIsSingleView}
/>
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,791 @@
import {
Button,
IconButton,
Popover,
TextField,
Typography,
} from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { getPoultryRequestsTotalQuantityService } from "../../../city/services/get-poultry-requests-total-quantity";
import { provinceCreateAutoAllocationService } from "../../services/province-create-auto-allocation";
import { provinceEditAutoAllocationService } from "../../services/province-edit-auto-allocation";
import { provinceGetAutoAllocationsService } from "../../services/province-get-auto-allocations";
import DeleteIcon from "@mui/icons-material/Delete";
import AddIcon from "@mui/icons-material/Add";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { ProvinceAddBuyerAutoAllocation } from "../province-add-buyer-auto-allocation/ProvinceAddBuyerAutoAllocation";
import { provinceDeleteAutoAllocationService } from "../../services/province-delete-auto-allocation";
import { provinceAddAutoAllocationService } from "../../services/province-add-auto-allocation";
import PrintIcon from "@mui/icons-material/Print";
import { provinceSubmitFinalAutoAllocationsService } from "../../services/province-submit-final-auto-allocations";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { ProvinceAutoSelectOptions } from "../province-auto-select-options/ProvinceAutoSelectOptions";
export const ProvinceAutoAllocationRequests = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [dataTable, setDataTable] = useState([]);
const [deletedRows, setDeletedRows] = useState([]);
const [addedRows, setAddedRows] = useState([]);
const [selectedProiority, setSelectedProiority] = useState({});
// const [allocationSumState, setAllocationSumState] = useState(0);
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const id = open ? "popoverr" : undefined;
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
useEffect(() => {
handleClose();
}, []);
const { poultryRequestsTotalQuantity } = useSelector(
(state) => state.citySlice
);
const { provinceGetAutoAllocations } = useSelector(
(state) => state.provinceSlice
);
useEffect(() => {
dispatch(provinceGetAutoAllocationsService());
}, []);
useEffect(() => {
setDataTable(deepCopy(provinceGetAutoAllocations));
}, [provinceGetAutoAllocations]);
const sendEditTableService = () => {
const combinedArr = dataTable?.reduce((acc, curr) => {
let d = deepCopy(curr.allocations);
d = d.filter((item) => item.allocationKey);
return [...acc, ...d];
}, []);
dispatch(
provinceDeleteAutoAllocationService({
allocation_delete_list: deletedRows?.filter(
(item) => item.allocationKey
),
})
).then(() => {
setDeletedRows([]);
dispatch(
provinceAddAutoAllocationService({
allocation_add_list: addedRows,
})
).then(() => {
dispatch(
provinceEditAutoAllocationService({
allocation_list: combinedArr,
})
).then((r) => {
setAddedRows([]);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(getPoultryRequestsTotalQuantityService());
dispatch(provinceGetAutoAllocationsService());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
});
});
};
const checkForDebt = () => {
// check if there are debt
const mapedDebts = dataTable.map((element) => {
const hasDebt = element.allocations.some((allocation) => allocation.debt);
return hasDebt;
});
const res = mapedDebts.some((item) => item);
return res;
};
const sendFinalTableService = () => {
const combinedArr = dataTable?.reduce((acc, curr) => {
let d = deepCopy(curr.allocations);
d = d.filter((item) => item.allocationKey);
return [...acc, ...d];
}, []);
dispatch(
provinceDeleteAutoAllocationService({
allocation_delete_list: deletedRows?.filter(
(item) => item.allocationKey
),
})
).then(() => {
setDeletedRows([]);
dispatch(
provinceAddAutoAllocationService({
allocation_add_list: addedRows,
})
).then(() => {
dispatch(
provinceEditAutoAllocationService({
allocation_list: combinedArr,
})
).then(() => {
setAddedRows([]);
const allocationsForCancel = provinceGetAutoAllocations?.reduce(
(total, item) => {
return [...total, ...item.allocations];
},
[]
);
const allocationsKeys = allocationsForCancel.map((item) => {
return { allocationKey: item.allocationKey };
});
dispatch(
provinceSubmitFinalAutoAllocationsService({
final_registration_list: allocationsKeys,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(getPoultryRequestsTotalQuantityService());
dispatch(provinceGetAutoAllocationsService());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
});
});
});
};
const sumOfAllocationQuantity =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return (
total +
item.allocations?.reduce((allocationTotal, allocation) => {
return allocationTotal + allocation.allocationQuantity;
}, 0)
);
}, 0);
const sumAllocationsSum =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return total + item.allocationsSum;
}, 0);
// const sumOfAllocationPercent = provinceGetAutoAllocations?.reduce(
// (total, item) => {
// return (
// total +
// item.allocations.reduce((allocationTotal, allocation) => {
// return allocationTotal + allocation.allocationPercent;
// }, 0)
// );
// },
// 0
// );
const sumOfKillRequests =
Array.isArray(dataTable) &&
dataTable?.reduce((total, item) => {
return total + item.poultryQuantity;
}, 0);
const [dateForAutoAlloction, setDateForAutoAllocation] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
useEffect(() => {
dispatch(getPoultryRequestsTotalQuantityService(dateForAutoAlloction));
}, [dateForAutoAlloction]);
const handleList = (selected) => {
const d = selected.map((item) => {
return {
[item.key]: item.value,
};
});
setSelectedProiority(d);
};
return (
<Grid
container
my={SPACING.SMALL}
direction="column"
alignItems="start"
xs={12}
pt={2}
px={2}
>
<SimpleTable
name={
<Grid xs={12} mb={2}>
<DatePicker
label="تاریخ تخصیص"
id="date"
renderInput={(params) => (
<TextField style={{ width: "180px" }} {...params} />
)}
value={dateForAutoAlloction}
onChange={(e) => {
setDateForAutoAllocation(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
}
columns={[
"تعداد کل درخواست کشتار",
"تخصیص داده شده",
"قابل تخصیص",
"تعداد خریداران آماده به تخصیص",
"تعداد مرغدار",
]}
data={[
[
`${poultryRequestsTotalQuantity?.acceptedQuantity?.toLocaleString()} قطعه`,
`${poultryRequestsTotalQuantity?.allocatedQuantity?.toLocaleString()} قطعه`,
`${poultryRequestsTotalQuantity?.assignableQuantity?.toLocaleString()} قطعه`,
`${poultryRequestsTotalQuantity?.numberOfKillHouse?.toLocaleString()} واحد`,
`${poultryRequestsTotalQuantity?.numberOfPoultry?.toLocaleString()} واحد`,
],
]}
/>
<Grid
container
gap={SPACING.SMALL}
my={SPACING.SMALL}
justifyContent="center"
alignItems="center"
width="100%"
>
<Grid
container
alignItems="center"
justifyContent="center"
direction="column"
gap={SPACING.SMALL}
width="100%"
>
<Typography fontWeight="bold">اولویت بندی کشتارگاه</Typography>
<ProvinceAutoSelectOptions handleList={handleList} />
<Grid>
<Button
variant="contained"
color="primary"
disabled={dataTable.length}
onClick={() => {
dispatch(
provinceCreateAutoAllocationService({
date: dateForAutoAlloction,
filter_list: selectedProiority,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(getPoultryRequestsTotalQuantityService());
dispatch(provinceGetAutoAllocationsService());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ایجاد تخصیص خودکار
</Button>
</Grid>
</Grid>
</Grid>
{!!dataTable?.length && (
<>
<Grid mb={SPACING.SMALL}>
<Typography fontWeight="bold" color="error">
* توجه: رنگ قرمز خریدار نشان دهنده بدهی قبلی است.
</Typography>
</Grid>
<table className="auto-allocation-table">
<tr>
<th>ردیف</th>
<th>واحد (مرغدار)</th>
<th>تعداد درخواست کشتار</th>
<th>وزن تقریبی درخواست کشتار</th>
<th>مانده قابل تخصیص</th>
<th>اضافه</th>
<th>ماهیت خریدار</th>
<th>خریدار</th>
<th>محل کشتار</th>
<th>تعداد تخصیص</th>
<th>جمع مرغ تخصیصی</th>
<th>سهم درصد</th>
<th>حواله مرغدار</th>
<th>حواله خریدار</th>
<th>حذف</th>
</tr>
{dataTable?.map((poultryRequestItem, j) => {
// const sumOfAllocation = poultryRequestItem.allocations?.reduce(
// (total, item) => {
// return total + item.allocationQuantity;
// },
// 0
// );
return poultryRequestItem?.allocations?.map((slaughter, i) => {
let reaminForAllocation =
poultryRequestItem?.poultryQuantity -
poultryRequestItem?.allocationsSum;
// if (reaminForAllocation < 0) {
// reaminForAllocation = (
// <Typography color={"#f2f2f2"}>{reaminForAllocation}</Typography>
// );
// }
return (
<tr key={i}>
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
{j + 1}
</td>
)}
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
{poultryRequestItem?.poultryName} (
{poultryRequestItem?.poultryAddress}) (
{poultryRequestItem?.poultryMobile})
</td>
)}
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
{poultryRequestItem?.poultryQuantity?.toLocaleString()}
</td>
)}
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
وزن تقریبی:{" "}
{poultryRequestItem?.poultryRequestWeight?.toLocaleString()}
<hr />
میانگین وزن:{" "}
{poultryRequestItem?.poultryRequestIndexWeight}
</td>
)}
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
{/* {poultryRequestItem?.poultryRemainQuantity} */}
{reaminForAllocation.toLocaleString()}
</td>
)}
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
<IconButton
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "اضافه کردن خریدار",
content: (
<ProvinceAddBuyerAutoAllocation
dataTable={dataTable}
poultryIndex={j}
setDataTable={setDataTable}
setAddedRows={setAddedRows}
/>
),
})
);
// setDataTable((prevState) => {
// // let edited = Array.from(prevState);
// let edited = deepCopy(prevState);
// edited[j].allocationsSum = edited[
// j
// ].allocations?.reduce((total, item) => {
// return total + item.allocationQuantity;
// }, 0);
// return edited;
// });
}}
>
<AddIcon />
</IconButton>
</td>
)}
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
color: slaughter.debt ? "red" : "",
}}
>
{slaughter?.type ? "کشتارکن" : "کشتارگاه"}{" "}
{slaughter.debt ? "(بدهکار)" : ""}
</td>
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
color: slaughter.debt ? "red" : "",
}}
>
{slaughter?.buyerName} ({slaughter?.buyerAddress}) (
{slaughter?.buyerMobile})
</td>
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
color: slaughter.debt ? "red" : "",
}}
>
{slaughter?.KillingPlace}
</td>
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
<TextField
style={{
width: "100px",
}}
type={"number"}
color={slaughter.debt ? "error" : ""}
inputProps={{
style: {
color: slaughter.debt ? "red" : "", // Set your desired color here
},
}}
value={slaughter?.allocationQuantity}
onChange={(e) => {
if (Number(e.target.value) < 1) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "در صورت نیاز به کاهش کمتر از 1 از گزینه حذف استفاده نمایید.",
severity: "error",
});
return;
}
setDataTable((prevState) => {
// let edited = Array.from(prevState);
let edited = deepCopy(prevState);
edited[j].allocations[i].allocationQuantity =
Number(e.target.value);
edited[j].allocationsSum = edited[
j
].allocations?.reduce((total, item) => {
return total + item.allocationQuantity;
}, 0);
return edited;
});
}}
/>
</td>
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
color: slaughter.debt ? "red" : "",
}}
>
{poultryRequestItem?.allocationsSum?.toLocaleString()}
{/* {sumOfAllocation} */}
</td>
)}
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
color: slaughter.debt ? "red" : "",
}}
>
%
{(
(slaughter?.allocationQuantity /
sumOfAllocationQuantity) *
100
).toFixed(2)}
</td>
{!i && (
<td
rowSpan={poultryRequestItem?.allocations.length}
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
<IconButton disabled={true} color="primary">
<a
href={`https://core-inventory.iran.liara.run/pdf/aviculture-remittance?poultryRequestKey=${poultryRequestItem.poultryRequestKey}&which=${window.location.origin}`}
>
<PrintIcon />
</a>
</IconButton>
</td>
)}
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
<IconButton disabled={true} color="primary">
<a
href={`https://core-inventory.iran.liara.run/pdf/buyer-remittance?allocationKey=${slaughter.allocationKey}&which=${window.location.origin}`}
>
<PrintIcon />
</a>
</IconButton>
</td>
<td
style={{
backgroundColor: j % 2 === 0 ? "#fff" : "#f2f2f2",
}}
>
<IconButton
color="error"
onClick={() => {
setDataTable((prevState) => {
// let edited = Array.from(prevState);
let edited = deepCopy(prevState);
setDeletedRows([
...deletedRows,
edited[j].allocations[i],
]);
edited[j].allocations.splice(i, 1);
edited[j].allocationsSum = edited[
j
].allocations?.reduce((total, item) => {
return total + item.allocationQuantity;
}, 0);
return edited;
});
}}
>
<DeleteIcon />
</IconButton>
</td>
</tr>
);
});
})}
<tr>
<td colSpan={"10"}>جمع کل</td>
<td>{sumOfAllocationQuantity.toLocaleString()}</td>
<td>{sumAllocationsSum.toLocaleString()}</td>
<td>
{/* {sumOfAllocationPercent} */}%
{((sumOfAllocationQuantity / sumOfKillRequests) * 100).toFixed(
2
)}
</td>
<td colSpan={3}>
<Button
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
fullWidth
>
عملیات
</Button>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div style={{ padding: "20px" }}>
<Grid container direction="column" gap={SPACING.TINY}>
<Button
variant="contained"
color="success"
onClick={() => {
sendEditTableService();
}}
>
ثبت موقت
</Button>
<Button
variant="contained"
color="error"
onClick={() => {
const allocationsForCancel =
provinceGetAutoAllocations?.reduce(
(total, item) => {
return [...total, ...item.allocations];
},
[]
);
const allocationsKeys = allocationsForCancel.map(
(item) => {
return { allocationKey: item.allocationKey };
}
);
dispatch(
provinceSubmitFinalAutoAllocationsService({
cancellation: allocationsKeys,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
getPoultryRequestsTotalQuantityService()
);
dispatch(provinceGetAutoAllocationsService());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
لغو تخصیص خودکار
</Button>
<Button
variant="contained"
onClick={() => {
if (checkForDebt()) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عدم بدهی در اولویت تخصیص خودکار شما می باشد. لطفا تخصیص را مدیریت نمایید.",
severity: "error",
});
handleClose();
} else {
sendFinalTableService();
handleClose();
}
}}
>
ثبت نهایی
</Button>
</Grid>
</div>
</Popover>
</td>
</tr>
</table>
</>
)}
{!dataTable.length && (
<Grid container>
<Typography>تخصیص خودکار ایجاد نشده است!</Typography>
</Grid>
)}
</Grid>
);
};
function deepCopy(obj) {
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
newObj[key] = deepCopy(obj[key]);
} else {
newObj[key] = obj[key];
}
}
return newObj;
}

View File

@@ -0,0 +1,33 @@
import { Tab, Tabs } from "@mui/material";
import { useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { ProvinceAutoAllocationArchive } from "../province-auto-allocation-archive/ProvinceAutoAllocationArchive";
import { ProvinceAutoAllocationRequests } from "../province-auto-allocation-requests/ProvinceAutoAllocationRequests";
import { ProvinceSlaughterhouseQuota } from "../province-slaughterhouse-quota/ProvinceSlaughterhouseQuota";
export const ProvinceAutoAllocation = () => {
const [activeTab, setActiveTab] = useState(0);
const handleTabChange = (event, newValue) => {
setActiveTab(newValue);
};
return (
<Grid container justifyContent="center" xs={12}>
<Grid container xs={12} justifyContent="center">
<Tabs
value={activeTab}
onChange={handleTabChange}
aria-label="simple tabs example"
>
<Tab label="تخصیص خودکار" />
<Tab label="سهم بندی کشتارگاه" />
<Tab label="بایگانی" />
</Tabs>
</Grid>
{activeTab === 0 && <ProvinceAutoAllocationRequests />}
{activeTab === 1 && <ProvinceSlaughterhouseQuota />}
{activeTab === 2 && <ProvinceAutoAllocationArchive />}
</Grid>
);
};

View File

@@ -0,0 +1,206 @@
import React, { useEffect } from "react";
import {
List,
ListItem,
ListItemText,
Button,
Checkbox,
ListItemIcon,
Paper,
TextField,
Typography,
} from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
export const ProvinceAutoSelectOptions = ({ handleList }) => {
function not(a, b) {
return a.filter((value) => b.indexOf(value) === -1);
}
function intersection(a, b) {
return a.filter((value) => b.indexOf(value) !== -1);
}
const [checked, setChecked] = React.useState([]);
const [left, setLeft] = React.useState([
{ label: "شهر", key: "city", value: true },
{ label: "عدم بدهی", key: "no_debt", value: true },
{ label: "حداقل تعداد تخصیص", key: "minimum_allocation", value: true },
]);
const [right, setRight] = React.useState([
{ label: "شهر محل کشتار", key: "kill_place", value: true },
]);
const leftChecked = intersection(checked, left);
const rightChecked = intersection(checked, right);
const handleToggle = (value) => () => {
const currentIndex = checked.indexOf(value);
const newChecked = [...checked];
if (currentIndex === -1) {
newChecked.push(value);
} else {
newChecked.splice(currentIndex, 1);
}
setChecked(newChecked);
};
const handleAllRight = () => {
setRight(right.concat(left));
setLeft([]);
};
const handleCheckedRight = () => {
setRight(right.concat(leftChecked));
setLeft(not(left, leftChecked));
setChecked(not(checked, leftChecked));
};
const handleCheckedLeft = () => {
setLeft(left.concat(rightChecked));
setRight(not(right, rightChecked));
setChecked(not(checked, rightChecked));
};
const handleAllLeft = () => {
setLeft(left.concat(right));
setRight([]);
};
const handleChange = (event) => {
const fieldToUpdateLeft = left.find(
(item) => item.key === "minimum_allocation"
);
const fieldToUpdateRight = right.find(
(item) => item.key === "minimum_allocation"
);
if (fieldToUpdateLeft) {
setLeft((prevState) => [
...prevState.filter((item) => item.key !== "minimum_allocation"),
{ ...fieldToUpdateLeft, value: event.target.value },
]);
} else if (fieldToUpdateRight) {
setRight((prevState) => [
...prevState.filter((item) => item.key !== "minimum_allocation"),
{ ...fieldToUpdateRight, value: event.target.value },
]);
}
};
useEffect(() => {
handleList(right);
}, [right]);
const customList = (items) => (
<Paper sx={{ width: "100%", height: 150, overflow: "auto" }}>
<List dense component="div" role="list">
{items.map((value, i) => {
const labelId = `transfer-list-item-${value}-label`;
return (
<ListItem key={value} role="listitem" onClick={handleToggle(value)}>
<Grid container alignItems="center">
<ListItemIcon>
<Checkbox
checked={checked.indexOf(value) !== -1}
tabIndex={-1}
disableRipple
inputProps={{
"aria-labelledby": labelId,
}}
/>
</ListItemIcon>
<ListItemText
id={labelId}
primary={`${i + 1}. ${value.label}`}
/>
{value.label === "حداقل تعداد تخصیص" && (
<Grid>
<TextField
label="تعداد"
type={"number"}
style={{ width: "100px" }}
value={value.value}
onChange={handleChange}
size={"small"}
/>
</Grid>
)}
</Grid>
</ListItem>
);
})}
</List>
</Paper>
);
return (
<Grid
container
spacing={2}
justifyContent="center"
alignItems="center"
mt={SPACING.TINY}
width="100%"
>
<Grid item flex={1}>
<Typography>انتخاب اولویت های تخصیص خودکار</Typography>
{customList(left)}
</Grid>
<Grid item flex={1}>
<Grid container direction="column" alignItems="center">
<Button
sx={{ my: 0.5 }}
variant="outlined"
size="large"
onClick={handleAllRight}
// disabled={left.length === 0}
disabled={true}
aria-label="move all right"
>
</Button>
<Button
sx={{ my: 0.5 }}
variant="outlined"
size="large"
onClick={handleCheckedRight}
// disabled={leftChecked.length === 0}
disabled={true}
aria-label="move selected right"
>
&gt;
</Button>
<Button
sx={{ my: 0.5 }}
variant="outlined"
size="large"
onClick={handleCheckedLeft}
// disabled={rightChecked.length === 0}
disabled={true}
aria-label="move selected left"
>
&lt;
</Button>
<Button
sx={{ my: 0.5 }}
variant="outlined"
size="large"
onClick={handleAllLeft}
// disabled={right.length === 0}
disabled={true}
aria-label="move all left"
>
</Button>
</Grid>
</Grid>
<Grid item flex={1}>
<Typography>اولویت های انتخاب شده تخصیص خودکار</Typography>
{customList(right)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,304 @@
import React, { useContext, useEffect, useState } from "react";
import { Button, IconButton, TextField } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { RiSearchLine } from "react-icons/ri";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { AppContext } from "../../../../contexts/AppContext";
import {
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { formatJustDate } from "../../../../utils/formatTime";
import { ProvinceSubmitBarDifference } from "../province-submit-bar-difference/ProvinceSubmitBarDifference";
import { ProvinceBarDifferencesOperations } from "../province-bar-differences-operations/ProvinceBarDifferencesOperations";
// import EmailIcon from "@mui/icons-material/Email";
import { provinceGetDashboardBarDiffrenceRequest } from "../../services/province-dashboard-bar-diffrence-get-request";
import { ProvinceBarDifferencesModal } from "../province-bar-differences-modal/ProvinceBarDifferencesModal";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const ProvinceBarDifferenceRequests = ({ state }) => {
const dispatch = useDispatch();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
try {
const response = await axios.get(
`bar-difference-request/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("province")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=${page}&page_size=${perPage}&state=${state}&date1=${selectedDate1}&date2=${selectedDate2}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
};
const fetchDashboardData = () => {
dispatch(
provinceGetDashboardBarDiffrenceRequest({
role: getRoleFromUrl(),
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
state: state,
filter: "search",
value: textValue,
date1: selectedDate1,
date2: selectedDate2,
})
).then((r) => {
if (r.payload?.data) {
setDashboardData(r.payload.data);
}
});
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
fetchDashboardData();
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.registerFullname,
formatJustDate(item?.createDate),
`${item?.hatching?.poultry?.unitName || ""} (${
item?.hatching?.poultry?.user?.mobile || ""
})`,
`${item?.killHouse?.name || ""} (${
item?.killHouse?.killHouseOperator?.user?.mobile || ""
})`,
item?.barInfo?.totalQuantity?.toLocaleString(),
item?.barInfo?.totalWeight?.toLocaleString(),
item?.barInfo?.firstTotalQuantity?.toLocaleString(),
item?.barInfo?.differenceQuantity?.toLocaleString(),
item?.quantity?.toLocaleString(),
item?.weight?.toLocaleString(),
item?.acceptorImages?.[0] ||
item?.acceptorMessage ||
item?.registerMessage ||
item?.violationImage?.[0] ? (
<Grid container key={i}>
<IconButton
variant="contained"
onClick={() => {
dispatch(
OPEN_MODAL({
content: <ProvinceBarDifferencesModal item={item} />,
title: "پیوست",
})
);
}}
>
</IconButton>
</Grid>
) : (
"-"
),
item.state === "pending"
? "در انتظار تایید"
: item.state === "rejected"
? "رد شده"
: "تایید شده",
state === "pending" ? (
<ProvinceBarDifferencesOperations
key={i}
updateTable={updateTable}
item={item}
/>
) : (
"-"
),
];
});
setTableData(d);
}, [data, state, page, perPage]);
useEffect(() => {
fetchApiData(1);
fetchDashboardData();
}, [state, perPage, selectedDate1, selectedDate2, selectedSubUser?.key]);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
fetchApiData(1);
fetchDashboardData();
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد",
"وزن تقریبی کشتار",
"حجم سفارشات دریافتی توسط کشتارگاه",
"اختلاف کشتار (حجم)",
"اختلاف حجم",
"اختلاف وزن",
]}
data={[
[
dashboardData?.count?.toLocaleString(),
dashboardData?.totalWeight?.toLocaleString(),
dashboardData?.firstTotalQuantity?.toLocaleString(),
dashboardData?.differenceQuantity?.toLocaleString(),
dashboardData?.quantity?.toLocaleString(),
dashboardData?.weight?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
{getRoleFromUrl() !== "KillHouse" && (
<Grid>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<ProvinceSubmitBarDifference updateTable={updateTable} />
),
title: " ثبت اختلاف کشتار",
})
);
}}
>
ثبت اختلاف کشتار
</Button>
</Grid>
)}
<Grid>
<DatePicker
label="از تاریخ"
renderInput={(params) => (
<TextField {...params} style={{ width: "160px" }} size="small" />
)}
value={selectedDate1}
onChange={(newValue) => {
setSelectedDate1(moment(newValue).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
renderInput={(params) => (
<TextField {...params} style={{ width: "160px" }} size="small" />
)}
value={selectedDate2}
onChange={(newValue) => {
setSelectedDate2(moment(newValue).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
customColors={[
{
name: "اختلاف حجم",
color: "#ff5555",
},
{
name: "اختلاف وزن",
color: "#ff5555",
},
]}
columns={[
"ردیف",
"ثبت کننده",
"تاریخ ثبت",
"مرغدار",
"کشتارگاه",
"حجم کم شده از سالن مرغدار",
"وزن تقریبی کشتار (کیلوگرم)",
"حجم سفارشات دریافتی توسط کشتارگاه",
" اختلاف کشتار(حجم)",
"اختلاف حجم",
"اختلاف وزن",
"پیوست",
"وضعیت",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={state === "pending" ? "اختلاف کشتار" : "بایگانی اختلاف کشتار"}
/>
</Grid>
);
};

View File

@@ -0,0 +1,35 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs } from "@mui/material";
import { ProvinceBarDifferenceRequests } from "../province-bar-difference-requests/ProvinceBarDifferenceRequests";
import { ProvinceBarDashboardService } from "../../services/province-bar-dashbored";
export const ProvinceBarDifference = () => {
const [value, setValue] = useState("0");
const dispatch = useDispatch();
const handleChange = (event, newValue) => {
setValue(newValue);
dispatch(
ProvinceBarDashboardService({
state: newValue === "0" ? "pending" : "archive",
})
);
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Tabs value={value} onChange={handleChange}>
<Tab value="0" label="جدید" />
<Tab value="1" label="بایگانی شده" />
</Tabs>
<Grid container xs={12} justifyContent="center" alignItem="center" mt={2}>
<ProvinceBarDifferenceRequests
state={value === "0" ? "pending" : "archive"}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,127 @@
import { useState, useEffect } from "react";
import { Tabs, Tab, TextField, Typography } from "@mui/material";
import ShowImage from "../../../../components/show-image/ShowImage";
import { Grid } from "../../../../components/grid/Grid";
export const ProvinceBarDifferencesModal = ({ item }) => {
const [tabIndex, setTabIndex] = useState(0);
const handleTabChange = (event, newValue) => {
setTabIndex(newValue);
};
const tabs = [];
if (item?.registerMessage || item?.violationImage?.[0]) {
tabs.push({ key: "register", label: "درخواست کننده" });
}
if (item?.acceptorMessage || item?.acceptorImage?.[0]) {
tabs.push({ key: "acceptor", label: "کشتارگاه" });
}
useEffect(() => {
if (tabIndex >= tabs.length) {
setTabIndex(0);
}
}, [tabs.length, tabIndex]);
return (
<Grid
container
xs={12}
flexDirection="column"
sx={{ width: "100%", justifyContent: "center", alignItems: "center" }}
>
<Tabs value={tabIndex} onChange={handleTabChange} centered>
{tabs.map((t, idx) => (
<Tab key={t.key} label={t.label} />
))}
</Tabs>
{tabs[tabIndex]?.key === "register" && (
<Grid container sx={{ padding: 2, width: "100%", marginTop: "10px" }}>
{item?.registerMessage && (
<TextField
label="پیغام درخواست کننده"
value={item?.registerMessage}
fullWidth
multiline
variant="outlined"
InputProps={{
readOnly: true,
style: { color: "black" },
}}
sx={{
backgroundColor: "white",
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "#c4c4c4",
},
}}
/>
)}
{item?.violationImage?.[0] && (
<Grid
item
mt={2}
sx={{
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: "5px",
}}
>
<Typography variant="subtitle2" sx={{ mb: 1 }}>
پیوست درخواست کننده:
</Typography>
<ShowImage src={item?.violationImage[0]} />
</Grid>
)}
</Grid>
)}
{tabs[tabIndex]?.key === "acceptor" && (
<Grid container sx={{ padding: 2, width: "100%" }}>
{item?.acceptorMessage && (
<TextField
label="پیغام کشتارگاه"
value={item?.acceptorMessage}
fullWidth
multiline
variant="outlined"
InputProps={{
readOnly: true,
style: { color: "black" },
}}
sx={{
backgroundColor: "white",
"& .MuiOutlinedInput-notchedOutline": {
borderColor: "#c4c4c4",
},
}}
/>
)}
{item?.acceptorImage?.[0] && (
<Grid
item
mt={2}
sx={{
width: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: "5px",
}}
>
<Typography variant="subtitle2" sx={{ mb: 1 }}>
پیوست کشتارگاه:
</Typography>
<ShowImage src={item?.acceptorImage[0]} />
</Grid>
)}
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,313 @@
import {
Button,
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useState, useContext } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import TuneIcon from "@mui/icons-material/Tune";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import SettingsSuggestIcon from "@mui/icons-material/SettingsSuggest";
import { ProvinceSubmitBarDifference } from "../province-submit-bar-difference/ProvinceSubmitBarDifference";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import {
provinceDeleteBarDifferenceService,
provinceEditBarDifferenceService,
} from "../../services/province-bar-differences-services";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { fixBase64 } from "../../../../utils/toBase64";
import { resizeImage } from "../../../../utils/resizeImage";
export const ProvinceBarDifferencesOperations = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = useState(null);
const [openNotif] = useContext(AppContext);
const handleClick = (event) => setAnchorEl(event.currentTarget);
const handleClose = () => setAnchorEl(null);
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const handleDelete = () => {
dispatch(
OPEN_MODAL({
title: "آیا مطمئن هستید؟",
content: (
<Grid container spacing={2}>
<Button
variant="contained"
onClick={() => {
dispatch(provinceDeleteBarDifferenceService(item?.key)).then(
(r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "success",
});
}
}
);
}}
>
تایید
</Button>
<Button onClick={() => dispatch(CLOSE_MODAL())}>لغو</Button>
</Grid>
),
})
);
handleClose();
};
const openAcceptRejectModal = () => {
dispatch(
OPEN_MODAL({
title: "تایید/ رد",
content: (
<AcceptRejectModal
item={item}
updateTable={updateTable}
openNotif={openNotif}
/>
),
})
);
handleClose();
};
return (
<div>
<IconButton aria-describedby={id} color="primary" onClick={handleClick}>
<TuneIcon />
</IconButton>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
transformOrigin={{ vertical: "top", horizontal: "left" }}
>
<List dense sx={{ py: 0, minWidth: 200 }}>
{getRoleFromUrl() !== "KillHouse" ? (
<>
<Tooltip title="ویرایش" placement="left-start">
<ListItem disablePadding>
<ListItemButton
sx={{ py: 0.5 }}
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<ProvinceSubmitBarDifference
item={item}
isEdit
updateTable={updateTable}
/>
),
title: "ویرایش اختلاف کشتار",
})
);
}}
>
<ListItemIcon sx={{ minWidth: 40 }}>
<EditIcon color="primary" fontSize="small" />
</ListItemIcon>
<ListItemText
primary="ویرایش اختلاف کشتار"
primaryTypographyProps={{
color: "primary",
variant: "body2",
}}
/>
</ListItemButton>
</ListItem>
</Tooltip>
<Tooltip title="حذف" placement="left-start">
<ListItem disablePadding>
<ListItemButton
sx={{ py: 0.5 }}
onClick={() => {
handleClose();
handleDelete();
}}
>
<ListItemIcon sx={{ minWidth: 40 }}>
<DeleteIcon color="error" fontSize="small" />
</ListItemIcon>
<ListItemText
primary="حذف اختلاف کشتار"
primaryTypographyProps={{
color: "error",
variant: "body2",
}}
/>
</ListItemButton>
</ListItem>
</Tooltip>
</>
) : (
<Tooltip title="تایید/ رد" placement="left-start">
<ListItem disablePadding>
<ListItemButton
sx={{ py: 0.5 }}
onClick={() => {
handleClose();
openAcceptRejectModal();
}}
>
<ListItemIcon sx={{ minWidth: 40 }}>
<SettingsSuggestIcon color="primary" fontSize="small" />
</ListItemIcon>
<ListItemText
primary="تایید/ رد اختلاف"
primaryTypographyProps={{
color: "primary",
variant: "body2",
}}
/>
</ListItemButton>
</ListItem>
</Tooltip>
)}
</List>
</Popover>
</div>
);
};
const AcceptRejectModal = ({ item, updateTable, openNotif }) => {
const dispatch = useDispatch();
const [acceptorMessage, setAcceptorMessage] = useState("");
const [profileImages, setProfileImages] = useState([]);
const [acceptorImages, setAcceptorImages] = useState([]);
const handleEditOrAccept = (newState) => {
dispatch(
provinceEditBarDifferenceService({
bar_key: item?.key,
kill_house_check: true,
acceptor_message: acceptorMessage,
acceptor_images: acceptorImages,
state: newState,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "success",
});
}
});
};
const handleImageChange = (imageList) => {
if (imageList.length === 0) {
setProfileImages([]);
setAcceptorImages([]);
return;
}
Promise.all(
imageList.map(
(image) =>
new Promise((resolve) =>
resizeImage(image.file, (resized) => resolve(fixBase64(resized)))
)
)
).then((resizedImages) => {
setProfileImages(imageList);
setAcceptorImages(resizedImages);
});
};
return (
<Grid container spacing={2} justifyContent="center">
<Grid item xs={12} justifyContent="center">
<Typography variant="body2" color="error">
توجه: در صورت تایید حجم قطعه اعلام شده، حجم مورد نظر از سالن مرغدار
کسر شده و با میانگین وزن کشتار مرغدار به انبار شما اضافه میگردد.
</Typography>
</Grid>
<Grid item xs={12} justifyContent="center">
<ImageUpload
onChange={handleImageChange}
images={profileImages}
maxNumber={1}
title="سند"
/>
</Grid>
<Grid item xs={12} justifyContent="center">
<TextField
label="توضیحات"
value={acceptorMessage}
onChange={(e) => setAcceptorMessage(e.target.value)}
fullWidth
/>
</Grid>
<Grid item>
<Button
variant="contained"
color="success"
onClick={() => handleEditOrAccept("accepted")}
>
تایید
</Button>
</Grid>
<Grid item>
<Button
variant="contained"
color="error"
onClick={() => handleEditOrAccept("rejected")}
>
رد
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,10 @@
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
export const ProvinceBarInfos = () => {
return (
<Grid>
<AdvancedTable />
</Grid>
);
};

View File

@@ -0,0 +1,616 @@
import { useDispatch, useSelector } from "react-redux";
import { useContext, useEffect, useState } from "react";
import { getProvinceBuyerStewardAllocationService } from "../../services/get-province-buyer-steward-allocation";
import { useParams } from "react-router-dom";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Button, TextField, Tooltip, Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import { SPACING } from "../../../../data/spacing";
import { provinceBuyerRealCarcassesService } from "../../services/province-buyer-real-carcasses";
import { AppContext } from "../../../../contexts/AppContext";
import { provinceAutomaticStewardAllocationService } from "../../services/province-automatic-steward-allocation";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceUpdateAllocateStewardsService } from "../../services/province-update-allocate-stewards";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { RiFileExcel2Fill } from "react-icons/ri";
import axios from "axios";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { slaughterGetUpdatedInventoryStock } from "../../../slaughter-house/services/salughter-get-updated-inventory-stock";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { formatJustDate } from "../../../../utils/formatTime";
import { ProvinceManageStewardAllocations } from "../province-manage-steward-allocations/ProvinceManageStewardAllocations";
import { provinceGetTotalReportAgentShareService } from "../../services/province-get-total-report-agent-share";
export const ProvinceBuyerStewardAllocation = () => {
const dispatch = useDispatch();
let currentDate = moment(new Date()).format("YYYY-MM-DD");
const { id, date } = useParams();
const [datePicker, setDatePicker] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
if (getRoleFromUrl() === "KillHouse") {
currentDate = datePicker;
} else {
currentDate = date;
}
const [dataTable, setDataTable] = useState([]);
const {
getProvinceBuyerStewardAllocation,
provinceGetTotalReportAgentShare,
} = useSelector((state) => state.provinceSlice);
const { slaughterUpdatedInventoryStock } = useSelector(
(state) => state.slaughterSlice
);
const updateInventory = () => {
dispatch(
slaughterGetUpdatedInventoryStock({
date: currentDate,
kill_house_key: id,
})
);
};
useEffect(() => {
dispatch(
provinceGetTotalReportAgentShareService({
date: currentDate,
kill_house_key: id,
})
);
if (getRoleFromUrl() === "KillHouse") {
dispatch(
provinceUpdateAllocateStewardsService({
kill_house_key: id,
date: currentDate,
})
).then(() => {
dispatch(
getProvinceBuyerStewardAllocationService({
date: currentDate,
killHouseKey: id,
})
).then(() => {
dispatch(
slaughterGetUpdatedInventoryStock({
date: currentDate,
kill_house_key: id,
})
);
});
});
}
if (getRoleFromUrl() === "ProvinceOperator") {
dispatch(
getProvinceBuyerStewardAllocationService({
date: currentDate,
killHouseKey: id,
})
).then(() => {
dispatch(
slaughterGetUpdatedInventoryStock({
date: currentDate,
kill_house_key: id,
})
);
});
}
}, [currentDate]);
const updateTable = () => {
dispatch(
getProvinceBuyerStewardAllocationService({
date: currentDate,
killHouseKey: id,
})
);
};
useEffect(() => {
const d = getProvinceBuyerStewardAllocation?.map((item) => {
return [
item.steward.guilds.guildsId,
// formatJustDate(item.steward.guilds.createDate),
item.steward.guilds.steward ? "مباشر" : "صنف",
item.steward.guilds.guildsName,
item.steward.guilds.user.fullname,
// item.steward?.guilds?.user?.nationalId,
item.steward?.guilds?.user?.mobile,
`${item.steward?.guilds?.typeActivity}/${item.steward?.guilds?.areaActivity}`,
item.steward?.guilds?.licenseNumber,
item.steward?.guilds?.address.city.name,
<Grid container key={item.key} direction="column">
<Grid>{`${item?.numberOfCarcasses?.toLocaleString()} قطعه`}</Grid>
<Grid>{`${item?.weightOfCarcasses?.toLocaleString()} کیلوگرم`}</Grid>
</Grid>,
<Grid container key={item.key} direction="column">
{item.state === "accepted" && (
<>
<Grid>{`${item?.realNumberOfCarcasses?.toLocaleString()} قطعه`}</Grid>
<Grid>{`${item?.realWeightOfCarcasses?.toLocaleString()} کیلوگرم`}</Grid>
</>
)}
{!item.systemRegistrationCode && (
<Grid>
<Button
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تعداد / وزن لاشه",
content: (
<RealCarcassesInput
key={item.key}
realWeightOfCarcasses={item.realWeightOfCarcasses}
realNumberOfCarcasses={item.realNumberOfCarcasses}
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
{item.state === "accepted" ? "ویرایش" : "وارد کردن اطلاعات"}
</Button>
</Grid>
)}
</Grid>,
// <RealCarcassesInput
// key={item.key}
// realWeightOfCarcasses={item.realWeightOfCarcasses}
// realNumberOfCarcasses={item.realNumberOfCarcasses}
// item={item}
// updateTable={updateTable}
// />,
<RegistrationInput
key={item.key}
loggedRegistrationCode={item.loggedRegistrationCode}
item={item}
updateTable={updateTable}
updateInventory={updateInventory}
/>,
];
});
setDataTable(d);
}, [getProvinceBuyerStewardAllocation]);
const [inventoryDate, setInventoryDate] = useState(new Date(currentDate));
useEffect(() => {
const tempDate = new Date(inventoryDate);
tempDate.setDate(new Date(currentDate).getDate() - 1);
setInventoryDate(tempDate);
}, [currentDate]);
return (
<Grid
container
alignItems="center"
justifyContent="start"
gap={SPACING.SMALL}
>
<Grid
container
alignItems="center"
justifyContent="start"
gap={SPACING.SMALL}
>
{/* <Typography>سهم بندی ها</Typography> */}
{getRoleFromUrl() === "KillHouse" && (
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "150px" }} {...params} />
)}
value={currentDate}
onChange={(e) => {
setDatePicker(moment(e).format("YYYY-MM-DD"));
}}
/>
)}
</Grid>
<Grid width="100%">
<SimpleTable
name={
<Grid container alignItems="center">
<Typography>
سهم بندی برای تاریخ کشتار مورخ {formatJustDate(inventoryDate)}{" "}
{provinceGetTotalReportAgentShare?.killer
? "کشتارکن"
: "کشتارگاه"}{" "}
{provinceGetTotalReportAgentShare?.name}
</Typography>
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}Broadcast_management/?date=${date}&kill_house_key=${id}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
}
columns={[
"حجم لاشه ها",
"وزن لاشه ها (کیلوگرم)",
"حجم لاشه تخصیص داده شده",
"وزن تخصیص داده شده (کیلوگرم)",
"حجم لاشه قابل تخصیص",
"وزن قابل تخصیص (کیلوگرم)",
]}
data={[
[
slaughterUpdatedInventoryStock?.totalNumberOfCarcasses?.toLocaleString(),
slaughterUpdatedInventoryStock?.totalWeightOfCarcasses?.toLocaleString(),
slaughterUpdatedInventoryStock?.allocatedTotalNumberOfCarcasses?.toLocaleString(),
slaughterUpdatedInventoryStock?.allocatedTotalWeightOfCarcasses?.toLocaleString(),
slaughterUpdatedInventoryStock?.remainTotalNumberOfCarcasses?.toLocaleString(),
slaughterUpdatedInventoryStock?.remainTotalWeightOfCarcasses?.toLocaleString(),
],
]}
/>
</Grid>
{(getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "SuperAdmin") && (
<ProvinceManageStewardAllocations />
)}
{getRoleFromUrl() === "KillHouse" && (
<AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
{/* <Typography>سهم بندی اتوماتیک</Typography>
{getRoleFromUrl() === "KillHouse" && (
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "150px" }} {...params} />
)}
value={date}
onChange={(e) => {
setDatePicker(moment(e).format("YYYY-MM-DD"));
}}
/>
)} */}
{/* <Button
variant="contained"
disabled={
getProvinceBuyerStewardAllocation?.length
? getProvinceBuyerStewardAllocation?.some(
(item) => item.state === "accepted"
)
: true
}
onClick={() => {
dispatch(
provinceAutomaticStewardAllocationService({
send_registration_code: true,
steward_allocation_list:
getProvinceBuyerStewardAllocation?.map(
(item) => item.key
),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ثبت و ارسال کداحراز
</Button> */}
</Grid>
}
columns={[
"شناسه صنف",
// "تاریخ ثبت",
"ماهیت",
"نام واحد صنفی",
"نام شخص/شرکت",
// "کدملی",
"موبایل",
"نوع/حوزه فعالیت",
"شماره مجوز",
"شهرستان",
"تعداد/وزن لاشه پیشنهادی",
"تعداد/وزن لاشه تحویلی",
"کداحراز",
]}
data={dataTable}
/>
)}
</Grid>
);
};
const validationSchema = Yup.object({
number1: Yup.number().required("این فیلد اجباری است"),
number2: Yup.number().required("این فیلد اجباری است"),
});
const RealCarcassesInput = ({
// realWeightOfCarcasses,
// realNumberOfCarcasses,
updateTable,
item,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
number1: item.realNumberOfCarcasses,
number2: item.realWeightOfCarcasses,
},
validationSchema: validationSchema,
onSubmit: (values) => {
// Handle form submission
// console.log(values);
},
});
// const [numberInput, setNumberInput] = useState(realNumberOfCarcasses);
// const [textInput, setTextInput] = useState(realWeightOfCarcasses);
// const handleNumberInputChange = (e) => {
// setNumberInput(e.target.value);
// };
// const handleTextInputChange = (e) => {
// setTextInput(e.target.value);
// };
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid
container
direction="column"
width="100%"
// flexWrap="nowrap"
gap={SPACING.SMALL}
>
<NumberInput
// value={textInput}
label="وزن (کیلوگرم)"
id="number2"
// size="small"
allowLeadingZeros
thousandSeparator=","
// onChange={handleTextInputChange}
// style={{ width: "85px" }}
// disabled={item.state === "accepted"}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.number2}
error={formik.touched.number2 && Boolean(formik.errors.number2)}
helperText={formik.touched.number2 && formik.errors.number2}
/>
<NumberInput
label="تعداد"
// value={numberInput}
id="number1"
// size="small"
// onChange={handleNumberInputChange}
allowLeadingZeros
thousandSeparator=","
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.number1}
error={formik.touched.number1 && Boolean(formik.errors.number1)}
helperText={formik.touched.number1 && formik.errors.number1}
// style={{ width: "75px" }}
// disabled={item.state === "accepted"}
/>
{/* {item.state !== "accepted" && ( */}
<Button
variant="contained"
disabled={!formik.isValid}
onClick={() => {
dispatch(
provinceBuyerRealCarcassesService({
confirm_information: true,
steward_allocation_key: item.key,
real_number_of_carcasses: Number(formik.values.number1),
real_weight_of_carcasses: Number(formik.values.number2),
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ثبت
</Button>
{/* )} */}
</Grid>
);
};
const RegistrationInput = ({
loggedRegistrationCode,
item,
updateTable,
updateInventory,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [textInput, setTextInput] = useState(loggedRegistrationCode);
const handleTextInputChange = (e) => {
setTextInput(e.target.value);
};
console.log(item.systemRegistrationCode, "zzzz");
return (
<Grid
container
width="100%"
flexWrap="nowrap"
alignItems="center"
justifyContent="center"
>
{item.systemRegistrationCode ? (
<Grid>
<TextField
value={textInput}
size="small"
onChange={handleTextInputChange}
style={{ width: "85px" }}
disabled={item?.finalRegistration}
/>
{!item?.finalRegistration && (
<Button
size="small"
onClick={() => {
dispatch(
provinceBuyerRealCarcassesService({
enter_registration_code: true,
steward_allocation_key: item.key,
logged_registration_code: textInput,
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ثبت
</Button>
)}
</Grid>
) : (
<Button
disabled={item.state !== "accepted"}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ارسال کداحراز",
content: (
<Grid container gap={SPACING.SMALL}>
<Typography>
در صورت ارسال کداحراز انجام هیچگونه عملیاتی مانند حذف و
ویرایش امکان پذیر نمی باشد.
</Typography>
<Grid
container
direction="column"
gap={SPACING.TINY}
width="100%"
>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
provinceAutomaticStewardAllocationService({
send_registration_code: true,
steward_allocation_list: [item.key],
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
updateTable();
updateInventory();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
ارسال کداحراز
</Button>
)}
</Grid>
);
};

View File

@@ -0,0 +1,222 @@
import { DatePicker } from "@mui/x-date-pickers";
import { Button, TextField, Tooltip } from "@mui/material";
import { useContext, useEffect, useState } from "react";
import moment from "moment";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import { totalReportDailyBroadCastService } from "../../../slaughter-house/services/salughter-total-report-daily-broad-cast";
import { getProvinceBuyersAllocationsService } from "../../services/get-province-buyers-allocations";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const ProvinceBuyersAllocations = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
// const navigate = useNavigate();
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState([]);
const { getProvinceBuyersAllocations, totalReportDailyBroadCast } =
useSelector((state) => state.provinceSlice);
useEffect(() => {
dispatch(
getProvinceBuyersAllocationsService({ selectedDate1, selectedDate2 })
);
dispatch(
totalReportDailyBroadCastService({
selectedDate1,
selectedDate2,
})
);
}, [selectedDate1, selectedDate2]);
useEffect(() => {
dispatch(
getProvinceBuyersAllocationsService({ selectedDate1, selectedDate2 })
);
dispatch(
totalReportDailyBroadCastService({
selectedDate1,
selectedDate2,
})
);
}, []);
useEffect(() => {
const d = getProvinceBuyersAllocations?.map((item, i) => {
return [
i + 1,
item?.informations?.buyers,
item?.informations?.city,
item?.informations?.numberOfSteward.toLocaleString(),
item?.informations?.numberOfGuild.toLocaleString(),
item?.informations?.incomingWeightOfColdHouse?.toLocaleString(),
item?.informations?.incomingQuantityOfColdHouse?.toLocaleString(),
item?.informations?.totalPreColdWeight?.toLocaleString(),
item?.informations?.totalPreColdQuantity?.toLocaleString(),
item?.informations?.totalNumberOfFreeCarcasses?.toLocaleString(),
item?.informations?.totalFreeWeightOfCarcasses?.toLocaleString(),
item?.informations?.totalWeightOfCarcasses?.toLocaleString(),
item?.informations?.totalNumberOfCarcasses?.toLocaleString(),
item?.informations?.finalTotalWeightOfCarcasses?.toLocaleString(),
item?.informations?.finalTotalNumberOfCarcasses?.toLocaleString(),
item?.informations?.totalAllocatedWeight?.toLocaleString(),
item?.informations?.totalAllocatedQuantity?.toLocaleString(),
item?.informations?.totalAcceptedAllocatedWeight?.toLocaleString(),
item?.informations?.totalAcceptedAllocatedQuantity?.toLocaleString(),
item?.informations?.totalRemainQuantity?.toLocaleString(),
item?.informations?.totalRemainWeight?.toLocaleString(),
<Tooltip key={i} title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}Broadcast_management/?date1=${selectedDate1}&date2=${selectedDate2}&kill_house_key=${item?.key}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={24} />
</Button>
</a>
</Tooltip>,
];
});
setDataTable(d);
}, [getProvinceBuyersAllocations]);
return (
<Grid>
<Grid
container
alignItems="center"
justifyContent="start"
gap={SPACING.SMALL}
mt={SPACING.TINY}
mb={SPACING.SMALL}
>
<Grid style={{ width: 160 }}>
<DatePicker
label="از تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid style={{ width: 160 }}>
<DatePicker
label="تا تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Tooltip title="خروجی اکسل">
<a
href={
dataTable?.length
? `${axios.defaults.baseURL}all_inventory_excel/?date1=${selectedDate1}&date2=${selectedDate2}`
: "#"
}
rel="noreferrer"
>
<Button disabled={!dataTable?.length} color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
<Grid mb={SPACING.SMALL} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
title={"خلاصه اطلاعات"}
columns={[
"تعداد خریداران",
"وزن ورودی از سردخانه",
"تعداد ورودی از سردخانه",
"تعداد پیش سرد",
"وزن پیش سرد",
"وزن خرید خارج از استان",
"تعداد خرید خارج از استان",
"وزن بارهای امروز",
"تعداد بارهای امروز",
"وزن کل انبار",
"تعداد کل انبار",
"وزن توزیع شده",
"تعداد توزیع شده",
"وزن توزیع تحویل شده",
"تعداد توزیع تحویل شده",
"وزن مانده در انبار",
"تعداد مانده در انبار",
"تعداد مباشر تخصیصی",
"تعداد صنف تخصیصی",
]}
data={[
[
totalReportDailyBroadCast?.buyers?.toLocaleString(),
totalReportDailyBroadCast?.incomingWeightOfColdHouse?.toLocaleString(),
totalReportDailyBroadCast?.incomingQuantityOfColdHouse?.toLocaleString(),
totalReportDailyBroadCast?.totalPreColdQuantity?.toLocaleString(),
totalReportDailyBroadCast?.totalPreColdWeight?.toLocaleString(),
totalReportDailyBroadCast?.totalFreeWeightOfCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.totalNumberOfFreeCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.totalWeightOfCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.totalNumberOfCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.finalTotalWeightOfCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.finalTotalNumberOfCarcasses?.toLocaleString(),
totalReportDailyBroadCast?.totalAllocatedWeight?.toLocaleString(),
totalReportDailyBroadCast?.totalAllocatedQuantity?.toLocaleString(),
totalReportDailyBroadCast?.totalAcceptedAllocatedWeight?.toLocaleString(),
totalReportDailyBroadCast?.totalAcceptedAllocatedQuantity?.toLocaleString(),
totalReportDailyBroadCast?.totalRemainQuantity?.toLocaleString(),
totalReportDailyBroadCast?.totalRemainWeight?.toLocaleString(),
totalReportDailyBroadCast?.numberOfSteward.toLocaleString(),
totalReportDailyBroadCast?.numberOfGuild.toLocaleString(),
],
]}
/>
</Grid>
<Grid
container
alignItems="center"
justifyContent="start"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
></Grid>
<ResponsiveTable
paginated
title="جزئیات پخش"
columns={[
"ردیف",
"خریدار",
"شهرستان",
"تعداد مباشر تخصیصی",
"تعداد صنف تخصیصی",
"وزن ورودی از سردخانه",
"تعداد ورودی از سردخانه",
"وزن پیش سرد",
"تعداد پیش سرد",
"تعداد خرید خارج از استان",
"وزن خرید خارج از استان",
"وزن بارهای امروز",
"تعداد بارهای امروز",
"وزن کل انبار",
"تعداد کل انبار",
"وزن توزیع شده",
"تعداد توزیع شده",
"وزن توزیع تحویل شده",
"تعداد توزیع تحویل شده",
"تعداد مانده در انبار",
"وزن مانده در انبار",
"جزئیات",
]}
data={dataTable}
/>
</Grid>
);
};

View File

@@ -0,0 +1,139 @@
import { Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { formatJustDate } from "../../../../utils/formatTime";
export const ProvinceCaseOverview = ({ item }) => {
return (
<Grid
container
direction={{ xs: "column", sm: "row" }}
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.SMALL}
>
<Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">مرغداری:</Typography>
<Typography variant="caption">{item?.poultry?.unitName}</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">مرغدار:</Typography>
<Typography variant="caption">
{item?.poultry?.user?.fullname}
</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">موبایل:</Typography>
<Typography variant="caption">
{item?.poultry?.user?.mobile}
</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">تاریخ کشتار:</Typography>
<Typography variant="caption">
{formatJustDate(item?.sendDate)}
</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">زمان و تاریخ ثبت:</Typography>
<Typography variant="caption">
{formatJustDate(item?.createDate)}
</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">شهرستان:</Typography>
<Typography variant="caption">
{item?.poultry?.address?.city?.name}
</Typography>
</Grid>
</Grid>
<Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">تعداد:</Typography>
<Typography variant="caption">
{item?.quantity?.toLocaleString()}
</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">متوسط وزن:</Typography>
<Typography variant="caption">{item?.IndexWeight}</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">وزن حدودی:</Typography>
<Typography variant="caption">{item?.hatching?.weight}</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">سن گله:</Typography>
<Typography variant="caption">{item?.hatching?.age}</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">نژاد:</Typography>
<Typography variant="caption">{item?.chickenBreed}</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">قیمت پیشنهادی:</Typography>
<Typography variant="caption">
{item?.amount?.toLocaleString()} ریال
</Typography>
</Grid>
</Grid>
<Grid>
{item?.provinceKillRequests?.provinceKillRequestSerializer.length ? (
<>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">خریداران:</Typography>
</Grid>
{item?.provinceKillRequests?.provinceKillRequestSerializer?.map(
(item, i) => {
return (
<Grid key={i} container gap={SPACING.TINY}>
<Typography variant="caption">{i + 1}- </Typography>
<Typography variant="caption">
{item?.killhouseUser?.name}
</Typography>
<Typography variant="caption">
({item?.killhouseUser?.killHouseOperator?.user?.mobile})
</Typography>
<Typography variant="caption">تعداد: </Typography>
<Typography variant="caption">
{item?.mainQuantity?.toLocaleString()}
</Typography>
<Typography variant="caption">محل کشتار: </Typography>
<Typography variant="caption">{item?.killPlace}</Typography>
</Grid>
);
}
)}
</>
) : (
<>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">خریداران: بدون خریدار</Typography>
</Grid>
</>
)}
</Grid>
<Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">قیمت مرغ:</Typography>
<Typography variant="caption">
{item?.amount?.toLocaleString()} ریال
</Typography>
</Grid>
{/* <Grid container gap={SPACING.SMALL}>
<Typography variant="caption">مبلغ قابل پرداخت:</Typography>
<Typography variant="caption">55112</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">باقی مانده قابل پرداخت:</Typography>
<Typography variant="caption">44</Typography>
</Grid>
<Grid container gap={SPACING.SMALL}>
<Typography variant="caption">مبلغ پرداخت شده:</Typography>
<Typography variant="caption">258522</Typography>
</Grid> */}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,511 @@
import {
// Divider,
// Paper,
Table,
TableBody,
TableCell,
TableRow,
TextField,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { provinceGetCaseStatusService } from "../../services/province-get-case-status";
export const ProvinceCaseStatusComponent = () => {
const dispatch = useDispatch();
const { provinceGetCaseStatus } = useSelector((state) => state.provinceSlice);
const [selectedDate, setSelectedDate] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const handleDateChange = (date) => {
setSelectedDate(date);
};
useEffect(() => {
dispatch(provinceGetCaseStatusService(selectedDate));
}, [selectedDate]);
return (
<Grid
container
direction="column"
alignItems="start"
width="1300px"
overflowX="scroll"
style={{ overflowX: "scroll" }}
>
<Grid>
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate}
onChange={(e) => {
handleDateChange(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<Grid>
{provinceGetCaseStatus?.map((item, i) => {
// city state color
let cityStateColor;
if (item?.cityState?.state === "pending") {
cityStateColor = "#ff7410";
}
if (item?.cityState?.state === "accept") {
cityStateColor = "green";
}
if (item?.cityState?.state === "reject") {
cityStateColor = "red";
}
// province state color
let provinceStateColor;
if (item?.provinceState?.state === "pending") {
provinceStateColor = "#ff7410";
}
if (item?.provinceState?.state === "accept") {
provinceStateColor = "green";
}
if (item?.provinceState?.state === "reject") {
provinceStateColor = "red";
}
return (
<Table
className="my-table"
key={i}
style={{ borderTop: "1px solid", marginTop: "10px" }}
>
<TableBody>
<TableRow
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell
component="th"
scope="row"
className="no-border-flex-table"
>
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
مرغدار
</Typography>
</Grid>
<Grid container direction="column">
<Typography variant="caption">
{item?.poultry?.poultryName} (
{item?.poultry?.userFullname})
</Typography>
<Typography variant="caption">
تلفن: {item?.poultry?.userMobile}
</Typography>
<Typography variant="caption">
{item?.poultry?.poultryRequestQuantity.toLocaleString()}{" "}
قطعه
</Typography>
<Typography variant="caption" color="blue">
دامپزشک فارم: {item?.poultry?.vetFarmName}
</Typography>
<Typography variant="caption" color="blue">
تلفن دامپزشک: {item?.poultry?.vetFarmMobile}
</Typography>
</Grid>
</TableCell>
<TableCell
component="th"
scope="row"
className="no-border-flex-table"
>
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
شهرستان
</Typography>
</Grid>
<Grid container direction="column">
<Typography variant="caption" color={cityStateColor}>
{item?.cityState?.operatorCity} (
{item?.cityState?.operatorName})
</Typography>
<Typography variant="caption" color={cityStateColor}>
تلفن: {item?.cityState?.operatorMobile}
</Typography>
</Grid>
</TableCell>
<TableCell
component="th"
scope="row"
className="no-border-flex-table"
>
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
استان
</Typography>
</Grid>
<Grid container direction="column">
<Typography
variant="caption"
color={provinceStateColor}
>
{item?.provinceState?.provinceOperatorProvinc} (
{item?.provinceState?.provinceOperatorName})
</Typography>
<Typography
variant="caption"
color={provinceStateColor}
>
تلفن: {item?.provinceState?.provinceOperatorMobile}
</Typography>
</Grid>
</TableCell>
<TableCell
component="th"
scope="row"
className="loop-kill-request no-border-flex-table"
>
{item?.provinceKillRequests?.map((killRequest, i) => {
// killhouse state color
let killHouseStateColor;
if (killRequest?.state === "pending") {
killHouseStateColor = "#ff7410";
}
if (killRequest?.state === "accepted") {
killHouseStateColor = "green";
}
if (killRequest?.state === "rejected") {
killHouseStateColor = "red";
}
return (
<TableRow
className="row-kill-request"
key={i}
sx={{
"&:last-child td, &:last-child th": { border: 0 },
}}
>
<TableCell component="th" scope="row">
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
خریدار
</Typography>
</Grid>
<Grid container direction="column">
<Typography
variant="caption"
color={killHouseStateColor}
>
{killRequest?.buyerName}
</Typography>
<Typography
variant="caption"
color={killHouseStateColor}
>
{killRequest?.quantity} قطعه
</Typography>
<Typography
variant="caption"
color={killHouseStateColor}
>
تلفن: {killRequest?.buyerMobile}
</Typography>
</Grid>
</TableCell>
<TableCell component="th" scope="row">
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
راننده
</Typography>
</Grid>
{killRequest?.killHouseRequests?.map((car, i) => {
let carStateColor = "";
if (car?.clearanceCode) {
carStateColor = "green";
} else {
carStateColor = "red";
}
return (
<Grid
container
direction="column"
key={i}
style={{ whiteSpace: "nowrap" }}
>
<Typography
variant="caption"
color={carStateColor}
>
{car?.driverName} / تعداد{" "}
{car?.quantity.toLocaleString()} /
کدبهداشتی {car?.trafficCode} / کدترخیص{" "}
{car?.clearanceCode
? car?.clearanceCode
: "در انتظار"}
</Typography>
</Grid>
);
})}
</TableCell>
<TableCell component="th" scope="row">
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
دامپزشک کشتارگاه
</Typography>
</Grid>
{killRequest?.killHouseRequests?.map((car, i) => {
let vetStateColor = "";
if (car?.vetState === "pending") {
vetStateColor = "#ff7410";
} else if (car?.vetState === "accepted") {
vetStateColor = "green";
} else {
vetStateColor = "red";
}
return (
<Grid
container
direction="column"
key={i}
style={{ whiteSpace: "nowrap" }}
>
<Typography
variant="caption"
color={vetStateColor}
>
{car?.killHouseVetName
? car?.killHouseVetName
: "ندارد"}{" "}
/{" "}
{car?.killHouseVetMobile
? car.killHouseVetMobile
: "ندارد"}
</Typography>
</Grid>
);
})}
</TableCell>
<TableCell component="th" scope="row">
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
اطلاعات بار
</Typography>
</Grid>
{killRequest?.killHouseRequests
?.filter((car) => car.bar)
?.map((car, i) => {
// city state color
let barStateColor;
if (car?.bar?.state === "pending") {
barStateColor = "#ff7410";
}
if (car?.bar?.state === "accepted") {
barStateColor = "green";
}
if (car?.bar?.state === "rejected") {
barStateColor = "red";
}
return (
<Grid
container
direction="column"
key={i}
style={{ whiteSpace: "nowrap" }}
>
<Typography
variant="caption"
color={barStateColor}
>
<Grid
container
gap={1}
style={{ flexFlow: "nowrap" }}
>
<a
href={
car?.bar?.carWeightWithLoadImage
}
>
<img
src={
car?.bar?.carWeightWithLoadImage
}
width="20"
height="20"
alt=""
style={{ borderRadius: "4px" }}
/>
</a>
وزن با بار:{" "}
{car?.bar?.carWeightWithLoad?.toLocaleString()}{" "}
/
<a
href={
car?.bar
?.carWeightWithoutLoadImage
}
>
<img
src={
car?.bar
?.carWeightWithoutLoadImage
}
width="20"
height="20"
alt=""
style={{ borderRadius: "4px" }}
/>
</a>
وزن بدون بار:{" "}
{car?.bar?.carWeightWithoutLoad?.toLocaleString()}{" "}
/ وزن خالص:
{car?.bar?.netWeight.toLocaleString()}{" "}
/ تعداد واقعی:
{car?.bar?.realQuantity.toLocaleString()}
</Grid>
</Typography>
</Grid>
);
})}
</TableCell>
<TableCell component="th" scope="row">
<Grid style={{ marginBottom: "2px" }}>
<Typography
variant="button"
textAlign="center"
style={{
borderBottom: "1px solid gray",
paddingBottom: "2px",
}}
>
فاکتور استان
</Typography>
</Grid>
{killRequest?.killHouseRequests
?.filter((car) => car.provinceFactor)
?.map((car, i) => {
// city state color
let provinceFactorStateColor;
if (
car?.killHouseFactor?.state === "pending"
) {
provinceFactorStateColor = "#ff7410";
}
if (
car?.killHouseFactor?.state === "accepted"
) {
provinceFactorStateColor = "green";
}
if (
car?.killHouseFactor?.state === "rejected"
) {
provinceFactorStateColor = "red";
}
return (
<Grid
container
direction="column"
key={i}
style={{ whiteSpace: "nowrap" }}
>
<Typography
variant="caption"
color={provinceFactorStateColor}
>
فی:{" "}
{car?.provinceFactor?.factorFee.toLocaleString()}
{" / "}
وزن:{" "}
{car?.provinceFactor?.totalWeight.toLocaleString()}
{" / "}
مبلغ کل:{" "}
{car?.provinceFactor?.totalPrice.toLocaleString()}
<a
href={
car?.killHouseFactor?.factorImage
}
>
<img
src={
car?.killHouseFactor?.factorImage
}
width="20"
height="20"
alt=""
style={{ borderRadius: "4px" }}
/>
</a>
شناسه پرداخت:
{car?.killHouseFactor?.paymentCode}
</Typography>
</Grid>
);
})}
</TableCell>
</TableRow>
);
})}
</TableCell>
</TableRow>
</TableBody>
</Table>
);
})}
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,5 @@
import { Typography } from "@mui/material";
export const ProvinceCaseStepBarsInfo = () => {
return <Typography>اطلاعات بارها</Typography>;
};

View File

@@ -0,0 +1,5 @@
import { Typography } from "@mui/material";
export const ProvinceCaseStepPaymentInformation = () => {
return <Typography>اطلاعات پرداخت</Typography>;
};

View File

@@ -0,0 +1,5 @@
import { Typography } from "@mui/material";
export const ProvinceCaseStepShipping = () => {
return <Typography>گواهی حمل</Typography>;
};

View File

@@ -0,0 +1,143 @@
import { Step, StepLabel, Stepper, Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useEffect, useState } from "react";
export const ProvinceCaseSteps = ({ stepClickedHandler, item }) => {
const [currentStep, setCurrentStep] = useState(0);
useEffect(() => {
let step = 0;
if (
item?.cityState?.state === "reject" ||
item?.provinceState?.state === "reject"
) {
step = -1;
} else {
if (item?.cityState?.date) {
step = 1;
}
if (item?.provinceState?.date) {
step = 2;
}
if (
item?.provinceKillRequests?.provinceKillRequestSerializer.length > 0
) {
step = 3;
}
if (item?.killHouseRequests?.killHouseRequestsSerializer.length > 0) {
step = 4;
}
if (
item?.killHouseRequests?.killHouseRequestVetCheckSerializer?.length > 0
) {
step = 5;
}
if (
item?.killHouseRequests?.killHouseRequestAssignmentSerializer?.length >
0
) {
step = 6;
}
}
setCurrentStep(step);
}, [item]);
const steps = item.out
? ["درخواست کشتار", "تایید شهرستان", "تایید استان"]
: [
"درخواست کشتار",
"تایید شهرستان",
"تایید استان",
"تخصیص به خریدار",
"ایجاد بار",
"تخلیه",
"اطلاعات بار",
"اطلاعات پرداخت",
];
const isStepFailed = (step) => {
let isFailed = false;
switch (step) {
case 1:
isFailed = item?.cityState?.state === "reject";
break;
case 2:
isFailed = item?.provinceState?.state === "reject";
break;
default:
break;
}
return isFailed;
};
return (
<Grid
container
width="100%"
mt={SPACING.MEDIUM}
justifyContent="start"
alignItems="center"
>
<Grid display={{ xs: "none", sm: "grid" }} width="100%">
<Stepper activeStep={currentStep} alternativeLabel>
{steps.map((label, index) => {
const labelProps = {};
if (isStepFailed(index)) {
labelProps.optional = (
<Typography variant="caption" color="error">
پرونده در این مرحله رد شد.
</Typography>
);
labelProps.error = true;
}
return (
<Step
sx={{ cursor: "pointer" }}
key={label}
onClick={() => {
if (currentStep >= index) {
stepClickedHandler(index);
}
}}
>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
</Grid>
<Grid display={{ xs: "block", sm: "none" }}>
<Stepper activeStep={currentStep} orientation="vertical">
{steps.map((label, index) => {
const labelProps = {};
if (isStepFailed(index)) {
labelProps.optional = (
<Typography variant="caption" color="error">
پرونده در این مرحله رد شد.
</Typography>
);
labelProps.error = true;
}
return (
<Step
key={label}
onClick={() => {
if (currentStep >= index) {
stepClickedHandler(index);
}
}}
>
<StepLabel {...labelProps}>{label}</StepLabel>
</Step>
);
})}
</Stepper>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,580 @@
import { Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
const styles = {
tableInNewPage: {
pageBreakAfter: "always",
paddingLeft: "40px",
paddingRight: "40px",
direction: "rtl",
fontFamily: "titr",
fontWeight: "bold",
},
container: {
width: "95%",
alignSelf: "center",
pageBreakInside: "avoid",
},
invoiceTable: {
width: "100%",
borderCollapse: "collapse",
alignSelf: "center",
fontFamily: "titr",
marginBottom: "5px",
marginTop: "15px",
borderRadius: "10px",
},
tableCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 12,
fontWeight: "bolder",
color: "#403e3e",
},
tableCellGreen: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 12,
color: "white",
fontWeight: "bolder",
backgroundColor: "rgba(26, 188, 156, 0.7)",
},
tableCellMobile: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 12,
},
tableInnerCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 9,
whiteSpace: "nowrap",
},
tableHeader: {
backgroundColor: "rgba(211, 211, 211, 0.3)",
pageBreakAfter: "auto",
},
headerRow: {
background: "linear-gradient(to right, #E684AE, #79CBCA, #77A1D3)",
backgroundColor: "rgba(232, 67, 147, 0.4)",
color: "#422020",
pageBreakInside: "avoid",
pageBreakAfter: "auto",
},
tableHeaderCell: {
fontSize: 14,
border: "1px solid #000",
padding: "4px",
textAlign: "center",
fontWeight: "bolder",
},
tableHeaderCellGreen: {
backgroundColor: "rgba(26, 188, 156, 0.7)",
fontSize: 12,
border: "1px solid #000",
padding: "4px",
textAlign: "center",
fontWeight: "bolder",
color: "white",
},
footer: {
left: 0,
bottom: 0,
width: "100%",
position: "absolute",
},
footerContainer: {
alignItems: "center",
display: "flex",
},
tableRowEven: {
backgroundColor: "rgba(170, 183, 255, 0.3)",
},
titleOfTable: {
marginRight: "20px",
fontSize: "15px",
},
tableCellAlert: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 12,
color: "red",
},
levelDetails: {
color: "red",
fontSize: 12,
},
};
export const TableCityLevel = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>مرحله شهرستان</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>اپراتور</th>
<th style={styles.tableHeaderCell}>سمت</th>
<th style={styles.tableHeaderCell}>تلفن</th>
<th style={styles.tableHeaderCell}>تعاونی مرغدار</th>
<th style={styles.tableHeaderCell}>آدرس</th>
<th style={styles.tableHeaderCell}>وضعیت</th>
</tr>
</thead>
{item?.cityState?.date ? (
<tbody>
<tr style={styles.tableRow}>
<td style={styles.tableCell}>
{item?.cityState?.cityOperatorFullname}
</td>
<td style={styles.tableCell}>اپراتور شهرستان</td>
<td style={styles.tableCell}>
{item?.cityState?.cityOperatorMobile}
</td>
<td style={styles.tableCell}>{item?.cityState?.poultry}</td>
<td style={styles.tableCell}>
{item?.cityState?.province + " - " + item?.cityState?.city}
</td>
<td style={styles.tableCell}>
{item?.cityState?.state === "accept" ? "تایید شده" : "رد شده"}
</td>
</tr>
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={6}>
هنوز شهرستان پرونده را تائید نکرده است
</td>
)}
</table>
</Grid>
);
};
export const TableProvinceLevel = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>مرحله استان</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>اپراتور</th>
<th style={styles.tableHeaderCell}>سمت</th>
<th style={styles.tableHeaderCell}>تلفن</th>
<th style={styles.tableHeaderCell}>تعاونی مرغدار</th>
<th style={styles.tableHeaderCell}>آدرس</th>
<th style={styles.tableHeaderCell}>وضعیت</th>
</tr>
</thead>
{item?.provinceState?.date ? (
<tbody>
<tr style={styles.tableRow}>
<td style={styles.tableCell}>
{item?.provinceState?.provinceOperatorFullname}
</td>
<td style={styles.tableCell}>اپراتور استان</td>
<td style={styles.tableCell}>
{item?.provinceState?.provinceOperatorMobile}
</td>
<td style={styles.tableCell}>{item?.provinceState?.poultry}</td>
<td style={styles.tableCell}>
{item?.provinceState?.province +
" - " +
item?.provinceState?.city}
</td>
<td style={styles.tableCell}>
{item?.provinceState?.state === "accept"
? "تایید شده"
: "رد شده"}
</td>
</tr>
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={6}>
هنوز استان پرونده را تائید نکرده است
</td>
)}
</table>
</Grid>
);
};
export const TableSalughterAllocationLevel = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>
مرحله تخصیص به خریدار {" ( "}
<span style={styles.levelDetails}>
تعداد درخواست:{" "}
{item?.provinceKillRequests?.provinceKillRequestsTotalInfo?.poultryRequestQuantity.toLocaleString()}
، تعداد تخصیصی به کشتارگاه:{" "}
{item?.provinceKillRequests?.provinceKillRequestsTotalInfo?.allocatedQuantity.toLocaleString()}
، مانده قابل تخصیصی:{" "}
{item?.provinceKillRequests?.provinceKillRequestsTotalInfo?.poultryRequestRemainQuantity.toLocaleString()}
</span>
{" ) "}
</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>ماهیت خریدار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>نوع تخصیص</th>
<th style={styles.tableHeaderCell}>تعداد تخصیصی</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>وزن تخصیصی</th>
<th style={styles.tableHeaderCell}>وضعیت</th>
</tr>
</thead>
{item?.provinceKillRequests?.provinceKillRequestSerializer ? (
<tbody>
{item?.provinceKillRequests?.provinceKillRequestSerializer.map(
(item, i) => (
<tr key={i} style={styles.tableRow}>
<td style={styles.tableCell}>{i + 1}</td>
<td style={styles.tableCell}>
{item?.killhouseUser?.killer === false
? "کشتارگاه"
: "کشتارکن"}
</td>
<td style={styles.tableCell}>
{`${item?.killhouseUser?.killHouseOperator?.user?.fullname} (${item?.killhouseUser?.killHouseOperator?.user?.mobile})`}
</td>
<td style={styles.tableCell}>
{item?.automatic === false ? "دستی" : "اتوماتیک"}
</td>
<td style={styles.tableCell}>
{item?.weightInfo?.provinceKillRequestQuantity}
</td>
<td style={styles.tableCell}>
{item?.weightInfo?.provinceKillRequestIndexWeight}
</td>
<td style={styles.tableCell}>
{item?.weightInfo?.provinceKillRequestWeight}
</td>
<td style={styles.tableCell}>
{item?.state === "accepted"
? "تایید شده"
: item?.state === "rejected"
? "رد شده"
: "در انتظار تایید"}
</td>
</tr>
)
)}
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={8}>
داده ای ثبت نشده!
</td>
)}
</table>
</Grid>
);
};
export const TableSlaughterAddCar = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>
مرحله ایجاد بار
{" ( "}
<span style={styles.levelDetails}>
تعداد تخصیص به خریداران:{" "}
{item?.killHouseRequests?.allocatedQuantity?.toLocaleString()}، تعداد
بارها: {item?.killHouseRequests?.numberOfBars?.toLocaleString()}،
تعداد تخصیص به ماشین:{" "}
{item?.killHouseRequests?.killHouseRequestQuantity?.toLocaleString()}،
مانده قابل تخصیص:{" "}
{item?.killHouseRequests?.remainQuantity?.toLocaleString()}
</span>
{" ) "}
</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>کد بار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>ماشین</th>
<th style={styles.tableHeaderCell}>راننده</th>
<th style={styles.tableHeaderCell}>تعداد</th>
<th style={styles.tableHeaderCell}>وزن بار</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>کد قرنطینه</th>
<th style={styles.tableHeaderCell}>محل کشتار</th>
</tr>
</thead>
{item?.killHouseRequests?.killHouseRequestsSerializer ? (
<tbody>
{item?.killHouseRequests?.killHouseRequestsSerializer.map(
(item, i) => (
<tr key={i} style={styles.tableRow}>
<td style={styles.tableCell}>{i + 1}</td>
<td style={styles.tableCell}>{item?.barCode}</td>
<td style={styles.tableCell}>
{`${item?.killhouseUser?.killHouseOperator?.user?.fullname} (${item?.killhouseUser?.killHouseOperator?.user?.mobile})`}
</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.typeCar} (${item?.addCar?.driver?.pelak})`}</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.driverName} (${item?.addCar?.driver?.driverMobile})`}</td>
<td style={styles.tableCell}>
{item?.quantity.toLocaleString()}
</td>
<td style={styles.tableCell}>{item?.weightInfo?.weight}</td>
<td style={styles.tableCell}>
{item?.weightInfo?.indexWeight}
</td>
<td style={styles.tableCell}>{item?.clearanceCode}</td>
<td style={styles.tableCell}>{item?.killPlace}</td>
</tr>
)
)}
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={10}>
داده ای ثبت نشده!
</td>
)}
</table>
</Grid>
);
};
export const TableSVetAcceptDischarge = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>
مرحله تخلیه بار
{" ( "}
<span style={styles.levelDetails}>
تعداد بارها: {item?.killHouseRequests?.numberOfBars?.toLocaleString()}
، تایید تخلیه:{" "}
{item?.killHouseRequests?.vetAcceptedNumberOfBars?.toLocaleString()}،
تخلیه نشده:{" "}
{item?.killHouseRequests?.vetRemainNumberOfBars?.toLocaleString()}
</span>
{" ) "}
</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>کد بار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>ماشین</th>
<th style={styles.tableHeaderCell}>راننده</th>
<th style={styles.tableHeaderCell}>تعداد</th>
<th style={styles.tableHeaderCell}>وزن بار</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>کد قرنطینه</th>
<th style={styles.tableHeaderCell}>تعداد تخلیه شده </th>
<th style={styles.tableHeaderCell}>وزن تخلیه شده </th>
<th style={styles.tableHeaderCell}>وضعیت</th>
<th style={styles.tableHeaderCell}>اطلاعات تخلیه</th>
</tr>
</thead>
{item?.killHouseRequests?.killHouseRequestVetCheckSerializer.length ? (
<tbody>
{item?.killHouseRequests?.killHouseRequestVetCheckSerializer.map(
(item, i) => (
<tr key={i} style={styles.tableRow}>
<td style={styles.tableCell}>{i + 1}</td>
<td style={styles.tableCell}>{item?.barCode}</td>
<td style={styles.tableCell}>
{`${item?.killhouseUser?.killHouseOperator?.user?.fullname} (${item?.killhouseUser?.killHouseOperator?.user?.mobile})`}
</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.typeCar} (${item?.addCar?.driver?.pelak})`}</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.driverName} (${item?.addCar?.driver?.driverMobile})`}</td>
<td style={styles.tableCell}>
{item?.quantity.toLocaleString()}
</td>
<td style={styles.tableCell}>{item?.weightInfo?.weight}</td>
<td style={styles.tableCell}>
{item?.weightInfo?.indexWeight}
</td>
<td style={styles.tableCell}>{item?.clearanceCode}</td>
<td style={styles.tableCell}>
{item?.vetAcceptedRealQuantity?.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.vetAcceptedRealWeight?.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.vetState === "accepted"
? "تخلیه شده"
: "در انتظار تخلیه"}
</td>
<td style={styles.tableCell}>
{item?.vetState === "accepted"
? `${item?.killHouseVet?.fullname} (${item?.killHouseVet?.mobile})`
: "-"}
</td>
</tr>
)
)}
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={13}>
تخلیه بار تایید نشده است.
</td>
)}
</table>
</Grid>
);
};
export const TableSlaughterInputBarData = ({ item }) => {
return (
<Grid mt={SPACING.SMALL}>
<Typography variant={"body2"}>اطلاعات بار</Typography>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>کد بار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>ماشین</th>
<th style={styles.tableHeaderCell}>راننده</th>
<th style={styles.tableHeaderCell}>تعداد</th>
<th style={styles.tableHeaderCell}>وزن بار</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>کد قرنطینه</th>
<th style={styles.tableHeaderCell}>تعداد تخلیه شده (دامپزشک)</th>
<th style={styles.tableHeaderCell}>وزن تخلیه شده (دامپزشک)</th>
<th style={styles.tableHeaderCell}>تعداد نهایی</th>
<th style={styles.tableHeaderCell}>وزن خالص واقعی</th>
</tr>
</thead>
{item?.killHouseRequests?.killHouseRequestAssignmentSerializer ? (
<tbody>
{item?.killHouseRequests?.killHouseRequestAssignmentSerializer.map(
(item, i) => (
<tr key={i} style={styles.tableRow}>
<td style={styles.tableCell}>{i + 1}</td>
<td style={styles.tableCell}>{item?.barCode}</td>
<td style={styles.tableCell}>
{`${item?.killhouseUser?.killHouseOperator?.user?.fullname} (${item?.killhouseUser?.killHouseOperator?.user?.mobile})`}
</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.typeCar} (${item?.addCar?.driver?.pelak})`}</td>
<td
style={styles.tableCell}
>{`${item?.addCar?.driver?.driverName} (${item?.addCar?.driver?.driverMobile})`}</td>
<td style={styles.tableCell}>
{item?.quantity.toLocaleString()}
</td>
<td style={styles.tableCell}>{item?.weightInfo?.weight}</td>
<td style={styles.tableCell}>
{item?.weightInfo?.indexWeight}
</td>
<td style={styles.tableCell}>{item?.clearanceCode}</td>
<td style={styles.tableCell}>
{item?.vetAcceptedRealQuantity?.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.vetAcceptedRealWeight?.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.acceptedRealQuantity?.toLocaleString()}
</td>{" "}
<td style={styles.tableCell}>
{item?.acceptedRealWeight?.toLocaleString()}
</td>
</tr>
)
)}
</tbody>
) : (
<td style={styles.tableCellAlert} colSpan={13}>
داده ای ثبت نشده!
</td>
)}
</table>
</Grid>
);
};
export const TableSlaughterFinanceInfo = ({ item }) => {
return (
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>کد بار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>ماشین</th>
<th style={styles.tableHeaderCell}>راننده</th>
<th style={styles.tableHeaderCell}>نژاد</th>
<th style={styles.tableHeaderCell}>کد قرنطینه</th>
<th style={styles.tableHeaderCell}>تعداد نهایی</th>
<th style={styles.tableHeaderCell}>وزن خالص بار</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>مبلغ فاکتور</th>
<th style={styles.tableHeaderCell}>وضعیت</th>
<th style={styles.tableHeaderCell}>اطلاعات پرداخت (شناسه پرداخت)</th>
</tr>
</thead>
{item?.provinceState?.date && (
<tbody>
<tr style={styles.tableRow}>
{/* <td style={styles.tableCell}>
{item?.provinceState?.provinceOperatorFullname}
</td> */}
</tr>
</tbody>
)}
</table>
);
};
export const TableInspector = ({ item }) => {
return (
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>کد بار</th>
<th style={styles.tableHeaderCell}>خریدار</th>
<th style={styles.tableHeaderCell}>ماشین</th>
<th style={styles.tableHeaderCell}>راننده</th>
<th style={styles.tableHeaderCell}>نژاد</th>
<th style={styles.tableHeaderCell}>کد قرنطینه</th>
<th style={styles.tableHeaderCell}>تعداد نهایی</th>
<th style={styles.tableHeaderCell}>وزن خالص بار</th>
<th style={styles.tableHeaderCell}>میانگین وزن</th>
<th style={styles.tableHeaderCell}>مبلغ فاکتور</th>
<th style={styles.tableHeaderCell}>وضعیت پرداخت</th>
<th style={styles.tableHeaderCell}>وضعیت</th>
<th style={styles.tableHeaderCell}>نظر بازرس</th>
</tr>
</thead>
{item?.provinceState?.date && (
<tbody>
<tr style={styles.tableRow}>
{/* <td style={styles.tableCell}>
{item?.provinceState?.provinceOperatorFullname}
</td> */}
</tr>
</tbody>
)}
</table>
);
};

View File

@@ -0,0 +1,253 @@
import {
Collapse,
IconButton,
Paper,
Tooltip,
Typography,
} from "@mui/material";
import { useEffect, useState, useRef, useContext } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { ProvinceCaseOverview } from "../province-case-overview/ProvinceCaseOverview";
import { ProvinceCaseSteps } from "../province-case-steps/ProvinceCaseSteps";
import { ProvinceCaseStepPaymentInformation } from "../province-case-step-payment-information/ProvinceCaseStepPaymentInformation";
import { BsFillFileEarmarkPdfFill } from "react-icons/bs";
import { useReactToPrint } from "react-to-print";
import { AppContext } from "../../../../contexts/AppContext";
import ProvinceGetCaseFile from "../province-get-case-file/ProvinceGetCaseFile";
import { formatJustDate } from "../../../../utils/formatTime";
import {
TableCityLevel,
TableProvinceLevel,
TableSVetAcceptDischarge,
TableSalughterAllocationLevel,
TableSlaughterAddCar,
TableSlaughterInputBarData,
} from "./Pages";
export const ProvinceCasesItem = ({ caseData, caseIndex, pageIndex, data }) => {
const [isCollapsed, setIsCollapsed] = useState(false);
const [clickedStep, setClickedStep] = useState(null);
const [statusColor, setStatusColor] = useState("black");
const [, , selectedDate1] = useContext(AppContext);
const [currentStep, setCurrentStep] = useState("تایید شهرستان");
useEffect(() => {
let step = "تایید شهرستان";
if (
caseData?.cityState?.state === "reject" ||
caseData?.provinceState?.state === "reject"
) {
step = "درخواست رد شد";
setStatusColor("red");
} else {
if (caseData?.cityState?.date) {
step = "تایید شهرستان";
}
if (caseData?.provinceState?.date) {
step = "تایید استان";
}
if (
caseData?.provinceKillRequests?.provinceKillRequestSerializer?.length >
0
) {
step = "تخصیص به خریدار";
}
if (
caseData?.killHouseRequests?.killHouseRequestsSerializer?.length > 0
) {
step = "ایجاد بار";
}
if (
caseData?.killHouseRequests?.killHouseRequestVetCheckSerializer
?.length > 0
) {
step = "تخلیه بار";
}
if (
caseData?.killHouseRequests?.killHouseRequestAssignmentSerializer
?.length > 0
) {
step = "اطلاعات بار";
}
}
setCurrentStep(step);
}, [caseData]);
const componentRef = useRef();
const printPDF = useReactToPrint({
content: () => componentRef.current,
documentTitle: `گزارش پرونده `,
});
const setPdfOptions = () => {
if (caseData) {
printPDF();
}
};
const handleToggleCollapse = () => {
setIsCollapsed(!isCollapsed);
};
const stepClickedHandler = (step) => {
setClickedStep(step);
};
let clickedStepComponent;
switch (clickedStep) {
case 1:
clickedStepComponent = <TableCityLevel item={caseData} />;
break;
case 2:
clickedStepComponent = <TableProvinceLevel item={caseData} />;
break;
case 3:
clickedStepComponent = <TableSalughterAllocationLevel item={caseData} />;
break;
case 4:
clickedStepComponent = <TableSlaughterAddCar item={caseData} />;
break;
case 5:
clickedStepComponent = <TableSVetAcceptDischarge item={caseData} />;
break;
case 6:
clickedStepComponent = <TableSlaughterInputBarData item={caseData} />;
break;
case 7:
clickedStepComponent = <ProvinceCaseStepPaymentInformation />;
break;
default:
break;
}
return (
<Paper>
<Grid container direction="row" mb={2}>
<div
style={{
width: "3%",
background: caseData.out ? "rgb(255 123 123)" : "rgb(172 235 230)",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexDirection: "column",
}}
>
<Typography color={caseData?.out ? "white" : "grey"}>
{pageIndex + caseIndex}.
</Typography>
<div
style={{
background: caseData.out
? "rgb(255 123 123)"
: "rgb(172 235 230)",
writingMode: "vertical-rl",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "100%",
}}
>
<Typography
style={{
background: "#2196f3",
borderRadius: "10px",
padding: "2px",
color: "white",
fontSize: "10px",
}}
>
{caseData.out ? "خارج استان" : "داخل استان"}
</Typography>
</div>
<div style={{ height: "10px" }}></div>
</div>
<div style={{ width: "97%" }}>
<Grid
style={{
backgroundColor:
caseIndex % 2 === 0 ? "white" : "rgb(247, 249, 250)",
padding: "10px",
cursor: "pointer",
width: "100%",
}}
>
<div style={{ visibility: "hidden", position: "absolute" }}>
<ProvinceGetCaseFile
ref={componentRef}
item={caseData}
inDate={selectedDate1}
reportType={"slaughter"}
/>
</div>
<Grid
container
onClick={handleToggleCollapse}
justifyContent="space-between"
alignItems="center"
height="100px"
>
<Grid xs={2}>
<Typography variant="body2">
شماره سفارش: {caseData?.orderCode}
</Typography>
</Grid>
<Grid xs={2}>
<Typography variant="body2" color={statusColor}>
نوع کشتار: {caseData?.freezing ? "انجماد" : "عادی"}
</Typography>
</Grid>
<Grid xs={2}>
<Typography variant="body2">
تاریخ کشتار: {formatJustDate(caseData?.sendDate)}
</Typography>
</Grid>
<Grid xs={2}>
<Typography variant="body2">
مرغدار: {caseData?.poultry?.unitName}
</Typography>
</Grid>
<Grid xs={2}>
<Typography variant="body2" color={statusColor}>
وضعیت: {currentStep}
</Typography>
</Grid>
<Grid xs={1}>
<Tooltip title="خروجی PDF">
<IconButton
aria-label="delete"
color="success"
onClick={setPdfOptions}
>
<BsFillFileEarmarkPdfFill size={25} />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
<Collapse in={isCollapsed}>
<Grid style={{ padding: "20px" }}>
<ProvinceCaseOverview item={caseData} />
<ProvinceCaseSteps
stepClickedHandler={stepClickedHandler}
item={caseData}
/>
{clickedStepComponent}
</Grid>
</Collapse>
</div>
</Grid>
</Paper>
);
};

View File

@@ -0,0 +1,290 @@
import {
Button,
Pagination,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import { useContext, useEffect, useState } from "react";
import { SPACING } from "../../../../data/spacing";
import { ProvinceCasesItem } from "../province-cases-item/ProvinceCasesItem";
import { useDispatch, useSelector } from "react-redux";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceGetCasesOverview } from "../../services/get-cases-overview";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const ProvinceCases = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [textValue, setTextValue] = useState("");
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const dispatch = useDispatch();
const { casesOverview } = useSelector((item) => item.provinceSlice);
const fetchApiData = async (page) => {
setPage(1);
dispatch(LOADING_START());
const response = await axios.get(
`poultry_requests_for_total_information/?date1=${selectedDate1}&date2=${selectedDate2}&search=filter&value=${
textValue ? textValue : ""
}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
};
const [page, setPage] = useState(1);
const handleChangePage = (event, newPage) => {
setPage(newPage + 1, fetchApiData(newPage + 1));
};
// const handlePageChange = (page) => {
// fetchApiData(page, textValue);
// };
// const handlePerRowsChange = async (newPerPage, page) => {
// setLoading(true);
// let response = await axios.get(
// `Poultry_Request/?state=accepted&date1=${selectedDate1}&date2=${selectedDate2}&search=filter&value=${
// textValue ? textValue : ""
// }&page=${page}&page_size=${newPerPage}`
// );
// setData(response.data.results);
// setTotalRows(response.data.count);
// setPerPage(newPerPage);
// setLoading(false);
// };
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [openNotif] = useContext(AppContext);
useEffect(() => {
setPerPage(10);
fetchApiData(1);
dispatch(
provinceGetCasesOverview({ date1: selectedDate1, date2: selectedDate2 })
);
}, [selectedDate1, selectedDate2]);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
fetchApiData(1);
};
return (
<Grid container p={SPACING.TINY} xs={12}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>روند پرونده ها</Typography>
<Grid style={{ width: "150px" }}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid style={{ width: "150px" }}>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
{/* <form onSubmit={handleSubmit}> */}
<TextField
autoComplete="off"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
value={textValue}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
{/* </form> */}
<Tooltip title="خروجی اکسل">
{/* <a
href={`${axios.defaults.baseURL}poultry_request_report_excel/?start=${selectedDate1}&end=${selectedDate2}`}
rel="noreferrer"
> */}
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}poultry_request_report_excel/?start=${selectedDate1}&end=${selectedDate2}&role=${getRoleFromUrl()}&key=${userKey}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
<Tooltip title="خروجی اکسل گزارش جامع">
{/* <a
href={`${axios.defaults.baseURL}detail_of_killing_excel/?date1=${selectedDate1}&date2=${selectedDate2}`}
rel="noreferrer"
> */}
<Button
color="warning"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}detail_of_killing_excel/?date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}&key=${userKey}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<Grid width="100%" mt={SPACING.SMALL} isDashboard>
<Grid container mb={2}>
<ResponsiveTable
title={"خلاصه اطلاعات"}
noPagination
isDashboard
columns={[
"تعداد پرونده ها",
"تعداد مرغداران",
"درخواست های کشتار (حجم)",
"درخواست های کشتار (وزن)",
"تعداد پرونده خارج از استان",
"خارج استان (تعداد)",
"خارج استان (وزن)",
"تعداد تخصیصات",
"تعداد بار",
"تعداد خریداران",
"تعداد بار دارای کد قرنطینه",
"تعداد بار بدون کد قرنطینه",
"تعداد بار تخلیه شده توسط دامپزشک",
"تعداد بار تخلیه نشده توسط دامپزشک",
"تعداد بار تکمیل شده توسط کشتارگاه",
"تعداد بار تکمیل نشده توسط کشتارگاه",
"جزئیات کامل",
]}
data={[
[
casesOverview?.poultryRequests?.toLocaleString(),
casesOverview?.poultry?.toLocaleString(),
casesOverview?.poultryRequestsQuantity?.toLocaleString(),
casesOverview?.poultryRequestsWeight?.toLocaleString(),
casesOverview?.outPoultryRequest?.toLocaleString(),
parseInt(
casesOverview?.outPoultryRequestQuantity
)?.toLocaleString(),
parseInt(
casesOverview?.outPoultryRequestWeight
)?.toLocaleString(),
casesOverview?.provinceKillRequests?.toLocaleString(),
casesOverview?.killHouseRequests?.toLocaleString(),
casesOverview?.provinceKillRequestsBuyers?.toLocaleString(),
casesOverview?.killHouseRequestsWithQuarantineCode?.toLocaleString(),
casesOverview?.killHouseRequestsWithoutQuarantineCode?.toLocaleString(),
casesOverview?.killHouseRequestsWithKillHouseVetAccepted?.toLocaleString(),
casesOverview?.killHouseRequestsWithoutKillHouseVetAccepted?.toLocaleString(),
casesOverview?.killHouseRequestsWithKillHouseAssignment?.toLocaleString(),
casesOverview?.killHouseRequestsWithoutKillHouseAssignment?.toLocaleString(),
<Button
key="ss"
color="warning"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}detail_of_killing_excel/?date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}&key=${userKey}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={30} />
</Button>,
],
]}
/>
</Grid>
<Grid alignItems="center" justifyContent="center">
{data.map((item, i) => (
<ProvinceCasesItem
key={i}
caseData={item}
caseIndex={i}
pageIndex={page !== 1 ? page * perPage - 9 : 1}
data={data}
/>
))}
{!!data?.length && (
<Grid container justifyContent="center" mt={SPACING.SMALL}>
<Pagination
count={Math.ceil(totalRows / perPage)}
page={page}
variant="outlined"
onChange={(event, newPage) => {
handleChangePage(event, newPage - 1);
}}
/>
</Grid>
)}
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,129 @@
import { Button, TextField, Typography } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { provinceCheckChainAllocationService } from "../../services/province-check-chain-allocation";
export const ProvinceCheckChainAllocation = ({ item, fetchApiData }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [value, setValue] = useState("");
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<Grid container direction="column">
<Grid>
<Typography variant="h6" gutterBottom>
اطلاعات مرغدار
</Typography>
<Typography variant="subtitle1" gutterBottom>
نام: {item?.poultryHatching?.poultry?.unitName} (
{item?.poultryHatching?.poultry?.user?.mobile})
</Typography>
<Typography variant="subtitle1" gutterBottom>
شرکت زنجیره:{" "}
{`${item?.chainCompany?.name} (${item?.chainCompany?.user.mobile})`}
</Typography>
<Typography variant="subtitle1" gutterBottom>
نوع تخصیص: {item?.outProvince ? "خارج استان" : "داخل استان"}
</Typography>
<Typography variant="subtitle1" gutterBottom>
خریدار:{" "}
{item?.killHouse?.killHouseOperator?.user.fullname
? `${item?.killHouse?.killHouseOperator?.user.fullname} (${item?.killHouse?.killHouseOperator?.user.mobile})`
: `${item?.buyerName} (${item?.buyerMobile})`}
</Typography>
</Grid>
<Grid container gap={SPACING.TINY} mt={SPACING.SMALL}>
<TextField
label="یادداشت"
variant="outlined"
fullWidth
multiline
rows={4}
value={value}
onChange={handleChange}
/>
<Button
style={{ flex: 0.5 }}
variant="contained"
color="success"
onClick={() => {
dispatch(
provinceCheckChainAllocationService({
chain_allcation_key: item?.key,
check_allocation: true,
state: "accepted",
})
).then((r) => {
fetchApiData(1);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
style={{ flex: 0.5 }}
variant="contained"
color="error"
onClick={() => {
dispatch(
provinceCheckChainAllocationService({
chain_allcation_key: item?.key,
check_allocation: true,
state: "rejected",
})
).then((r) => {
fetchApiData(1);
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
رد
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,407 @@
import {
Button,
FormControl,
FormControlLabel,
FormLabel,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { provinceCheckFreeSaleService } from "../../services/province-check-free-sale";
import { provinceGetFreeSalesRequestsService } from "../../services/province-get-free-sales-requests";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { provinceEditFreeSaleService } from "../../services/province-edit-free-sale";
import { useProvinceName } from "../../../../utils/getProvinceName";
export const ProvinceCheckFreeSale = ({
buyer,
poultryRequestKey,
item,
isEdit,
}) => {
const [, , selectedDate1, , selectedDate2] = useContext(AppContext);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [value, setValue] = useState("");
const [payerValue, setPayerValue] = useState(
isEdit ? item.payerType : "poultry"
);
const handleChange = (event) => {
setValue(event.target.value);
};
const provinceName = useProvinceName();
const handleChangePayer = (event) => {
setPayerValue(event.target.value);
if (event.target.value === "buyer") {
formik.setFieldValue("mobile", buyer.mobile);
} else {
formik.setFieldValue("mobile", item.poultry.user.mobile);
}
};
const formik = useFormik({
initialValues: {
mobile: item.poultry.user.mobile,
weight: isEdit ? item?.IndexWeight : "",
quantity: isEdit ? item?.quantity : "",
},
validationSchema: Yup.object({
mobile: Yup.string()
.required("شماره موبایل الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
weight: Yup.number(),
quantity: Yup.number(),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
let newVal = formik.values.weight;
const mystring = formik.values.weight.toString().split(".").join("");
if (formik.values.weight) {
if (mystring.length <= 3) {
if (mystring.length === 2) {
newVal = mystring[0] + "." + mystring[1];
}
if (mystring.length === 3) {
newVal = mystring[0] + "." + mystring[1] + mystring[2];
}
}
}
if (isNaN(Number.parseFloat(newVal))) {
formik.setFieldValue("weight", "");
} else {
formik.setFieldValue("weight", Number.parseFloat(newVal));
}
}, [formik.values.weight]);
let buyerType = "-";
if (buyer.buyerType === "freezing") {
buyerType = "انجماد";
} else if (buyer.buyerType === "killer") {
buyerType = "کشتارکن";
} else if (buyer.buyerType === "killhouse") {
buyerType = "کشتارگاه";
}
const reg = new RegExp(/^09\d{9}$/);
return (
<Grid container direction="column">
<Grid>
<Typography variant="h6" gutterBottom>
اطلاعات خریدار
</Typography>
<Typography variant="subtitle1" gutterBottom>
نام: {buyer.firstName} {buyer.lastName}
</Typography>
<Typography variant="subtitle1" gutterBottom>
موبایل: {buyer.mobile}
</Typography>
<Typography variant="subtitle1" gutterBottom>
شهر: {buyer.city}
</Typography>
<Typography variant="subtitle1" gutterBottom>
استان: {buyer.province}
</Typography>
<Typography variant="subtitle1" gutterBottom>
ماهیت خریدار: {buyerType}
</Typography>
</Grid>
{provinceName !== "hamedan" && (
<>
<FormControl>
<FormLabel id="demo-controlled-radio-buttons-group">
پرداخت کننده
</FormLabel>
<RadioGroup
row
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={payerValue}
onChange={handleChangePayer}
>
<FormControlLabel
value="poultry"
control={<Radio />}
label="مرغدار"
/>
<FormControlLabel
value="buyer"
control={<Radio />}
label="خریدار"
/>
</RadioGroup>
</FormControl>
<Grid
container
xs={12}
alignItems="center"
justifyContent="center"
mt={1}
p={1}
gap={1}
style={{
borderStyle: "solid",
borderWidth: "2px",
borderColor: "gray",
borderRadius: "5px",
}}
>
{payerValue === "poultry" ? (
<Typography variant="body2" color="error">
{!reg.test(item.poultry.user.mobile)
? "فرمت تلفن مرغدار نادرست است! لطفا یک شماره موبایل معتبر وارد کنید."
: "از این قسمت میتوانید تلفن مرغدار را ویرایش کنید."}
</Typography>
) : (
<Typography variant="body2" color="error">
{!reg.test(buyer.mobile)
? "فرمت تلفن خریدار نادرست است! لطفا یک شماره موبایل معتبر وارد کنید."
: "از این قسمت میتوانید تلفن خریدار را ویرایش کنید."}
</Typography>
)}
<TextField
fullWidth
id="mobile"
value={formik.values.mobile}
error={
formik.touched.mobile ? Boolean(formik.errors.mobile) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.mobile && Boolean(formik.errors.mobile)
? formik.errors.mobile
: null
}
label="موبایل"
autoComplete="current-password"
variant="outlined"
/>
</Grid>
{isEdit && (
<Grid container xs={12} gap={1} mt={2}>
<Grid xs={12}>
<TextField
id="quantity"
fullWidth
label="حجم (قطعه)"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.quantity}
error={
formik.touched.quantity
? Boolean(formik.errors.quantity)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.quantity && Boolean(formik.errors.quantity)
? formik.errors.quantity
: null
}
/>
</Grid>
<Grid xs={12}>
<TextField
id="weight"
fullWidth
label="میانگین وزن"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.weight}
error={
formik.touched.weight ? Boolean(formik.errors.weight) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
/>
</Grid>
</Grid>
)}
</>
)}
<Grid container gap={SPACING.TINY} mt={SPACING.SMALL}>
{!isEdit && (
<TextField
label="یادداشت"
variant="outlined"
fullWidth
multiline
rows={4}
value={value}
onChange={handleChange}
/>
)}
{isEdit ? (
<Button
fullWidth
variant="contained"
color="primary"
disabled={provinceName !== "hamedan" ? !formik.isValid : false}
onClick={() => {
dispatch(
provinceEditFreeSaleService({
poultry_request_key: poultryRequestKey,
payer_type: payerValue,
buyer_mobile:
payerValue === "buyer" ? formik.values.mobile : null,
poultry_mobile:
payerValue === "poultry" ? formik.values.mobile : null,
Index_weight: formik.values.weight,
quantity: parseInt(formik.values.quantity),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
provinceGetFreeSalesRequestsService({
selectedDate1,
selectedDate2,
})
);
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
ویرایش
</Button>
) : (
<>
<Button
style={{ flex: 0.5 }}
variant="contained"
color="success"
disabled={provinceName !== "hamedan" ? !formik.isValid : false}
onClick={() => {
dispatch(
provinceCheckFreeSaleService({
poultry_request_key: poultryRequestKey,
state: "accepted",
message: value,
payer_type: payerValue,
buyer_mobile:
payerValue === "buyer" ? formik.values.mobile : null,
poultry_mobile:
payerValue === "poultry" ? formik.values.mobile : null,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
provinceGetFreeSalesRequestsService({
selectedDate1,
selectedDate2,
})
);
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
style={{ flex: 0.5 }}
variant="contained"
color="error"
onClick={() => {
dispatch(
provinceCheckFreeSaleService({
poultry_request_key: poultryRequestKey,
state: "rejected",
message: value,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
provinceGetFreeSalesRequestsService({
selectedDate1,
selectedDate2,
})
);
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
رد
</Button>
</>
)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,269 @@
import React, { useContext, useEffect, useState } from "react";
import { Button, Checkbox, TextField, Tooltip } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { useParams } from "react-router-dom";
import { formatJustDate } from "../../../../utils/formatTime";
import ToggleOffOutlinedIcon from "@mui/icons-material/ToggleOffOutlined";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
export const ProvinceChickenDistributionsAndSalesDetails = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const { unitkey, name, type } = useParams();
const [withDate, setWithDate] = useState(false);
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
let response;
dispatch(LOADING_START());
response = await axios.get(
`https://rsibackend.rasadyar.com/app/${
type === "Steward" ? "guilds-" : ""
}transport-carcass-detail/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${page}&page_size=${perPage}&code=${unitkey}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
// const updateTable = () => {
// fetchApiData(page !== 0 ? page : 1);
// };
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.productDate ? formatJustDate(item?.productDate) : "-",
`${formatJustDate(item?.date)} (${item?.time?.slice(0, 5)})`,
item?.out ? "خارج استان" : "داخل استان",
item?.product,
item?.quantity?.toLocaleString(),
<CheckCleanceCode clearanceCode={item?.tracking || "-"} key={i} />,
item?.origin,
item?.originProvince,
item?.originCity,
item?.destination,
item?.destinationProvince,
item?.destinationCity,
item?.driverName,
item?.owner,
item?.carTrackingCode,
item?.plate,
item?.unloading ? "تخلیه شده" : "در انتظار تخلیه",
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, selectedDate1, selectedDate2, perPage, withDate]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`https://rsibackend.rasadyar.com/app/${
type === "Steward" ? "guilds-" : ""
}transport-carcass-detail/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}&code=${unitkey}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "5px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
alignItems="center"
>
<Checkbox
icon={<ToggleOffOutlinedIcon />}
checkedIcon={<ToggleOnIcon />}
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
size="large"
/>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
<Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `https://rsibackend.rasadyar.com/app/${
type === "Steward" ? "guilds-" : ""
}transport-carcass-detail-excel/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&code=${unitkey}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ توزیع",
"تاریخ ثبت",
"نوع بار",
"محصول",
"وزن",
"کد قرنطینه",
"فروشنده",
"استان فروشنده",
"شهر فروشنده",
"خریدار",
"استان خریدار",
"شهر خریدار",
"راننده",
"مالک",
"رهگیری خودرو",
"پلاک خودرو",
"وضعیت",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={`توزیع / فروش گوشت ${name} `}
/>
</Grid>
);
};

View File

@@ -0,0 +1,437 @@
import React, { useContext, useEffect, useState } from "react";
import {
Autocomplete,
Button,
Checkbox,
IconButton,
MenuItem,
Select,
TextField,
Tooltip,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { getSamasatProvinces } from "../../../../utils/getSamasatProvinces";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import {
ROUTE_ADMINX_ROUTE_NATIONAL_INFO_SLAUGHTER,
ROUTE_SUPER_ADMIN_ROUTE_NATIONAL_INFO_SLAUGHTER,
} from "../../../../routes/routes";
import { ProvinceChickenDistributionsAndSalesDashboardService } from "../../services/province-chicken-distribution-and-sales-dashboard";
import { formatTime } from "../../../../utils/formatTime";
import ToggleOffOutlinedIcon from "@mui/icons-material/ToggleOffOutlined";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import VisibilityIcon from "@mui/icons-material/Visibility";
export const ProvinceChickenDistributionsAndSales = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
const [selectedRole, setSelectedRole] = useState("KillHouse");
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const [dashboardData, setDashboardData] = useState([]);
const [withDate, setWithDate] = useState(false);
const [openNotif] = useContext(AppContext);
const [selectedProvince, setSelectedProvince] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
let response;
getDashboardData();
dispatch(LOADING_START());
response = await axios.get(
`https://rsibackend.rasadyar.com/app/transport-carcass-detail/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&role=${selectedRole}&province=${selectedProvince}&page=${page}&page_size=${perPage}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
// const updateTable = () => {
// fetchApiData(page !== 0 ? page : 1);
// };
const getDashboardData = () => {
dispatch(
ProvinceChickenDistributionsAndSalesDashboardService({
role: selectedRole,
province: selectedProvince,
date1: withDate ? selectedDate1 : null,
date2: withDate ? selectedDate2 : null,
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
const d = data?.map((item, i) => {
const distributonWeight =
item?.info?.totalInputBarsWight + item?.info?.totalOutputBarsWight;
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.info?.role,
item?.UnitName || item?.name,
item?.PartIdCode || item?.jihadiCode,
item?.Province || item?.province,
item?.City || item?.city,
"گوشت مرغ تازه",
item?.info?.totalInputBuyBarsCount?.toLocaleString(),
item?.info?.totalInputBuyBarsWight?.toLocaleString(),
item?.info?.totalOutputBuyBarsCount?.toLocaleString(),
item?.info?.totalOutputBuyBarsWight?.toLocaleString(),
item?.info?.totalWareHouse?.toLocaleString(),
distributonWeight?.toLocaleString(),
"%" +
(item?.info?.totalWareHouse
? ((distributonWeight / item?.info?.totalWareHouse) * 100).toFixed(
1
)
: 0),
item?.info?.inputBars?.toLocaleString(),
item?.info?.totalInputBarsWight?.toLocaleString(),
"%" + item?.info?.totalInputBarsPercent?.toLocaleString(),
item?.info?.totalOutputBarsWight?.toLocaleString(),
"%" + item?.info?.totalOutputBarsPercent?.toLocaleString(),
<IconButton
key={i}
color="success"
size="small"
onClick={() => {
window.open(
getRoleFromUrl() === "AdminX"
? `${ROUTE_ADMINX_ROUTE_NATIONAL_INFO_SLAUGHTER}/${
selectedRole === "KillHouse"
? item.PartIdCode
: item?.jihadiCode
}/${item?.UnitName || item?.name}/distribution`
: `${ROUTE_SUPER_ADMIN_ROUTE_NATIONAL_INFO_SLAUGHTER}/${
selectedRole === "KillHouse"
? item.PartIdCode
: item?.jihadiCode
}/${item?.UnitName || item?.name}/distribution`,
"_blank"
);
}}
>
<VisibilityIcon />
</IconButton>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [
dispatch,
selectedDate1,
selectedDate2,
perPage,
selectedRole,
selectedProvince,
withDate,
]);
const handleSubmit = async (event) => {
event.preventDefault();
getDashboardData();
dispatch(LOADING_START());
try {
const response = await axios.get(
`https://rsibackend.rasadyar.com/app/transport-carcass-detail/?role=${selectedRole}&province=${selectedProvince}&search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const getProvinceList = () => {
return [{ name: "همه" }, ...getSamasatProvinces()];
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"نقش",
"تعداد",
"محصول",
"وزن خرید داخل استان",
"وزن خرید خارج استان",
"وزن کل انبار",
"وزن کل توزیع",
"درصد توزیع نسبت به انبار",
"تعداد توزیع داخل استان",
"وزن توزیع داخل استان",
"درصد توزیع داخل استان",
"تعداد توزیع خارج استان",
"وزن توزیع خارج استان",
"درصد توزیع خارج استان",
"آخرین آپدیت",
]}
data={[
[
dashboardData?.role,
selectedRole === "KillHouse"
? dashboardData?.totalCountKillHouse?.toLocaleString()
: dashboardData?.totalCountSteward?.toLocaleString(),
dashboardData?.product,
dashboardData?.totalInputBuyBarsWight?.toLocaleString(),
dashboardData?.totalOutputBuyBarsWight?.toLocaleString(),
dashboardData?.totalWareHouse?.toLocaleString(),
(
dashboardData?.totalInputBarsWight +
dashboardData?.totalOutputBarsWight
)?.toLocaleString(),
dashboardData?.totalWareHouse
? "%" +
(dashboardData?.totalWareHouse
? (
((dashboardData?.totalInputBarsWight +
dashboardData?.totalOutputBarsWight) /
dashboardData?.totalWareHouse) *
100
).toFixed(1)
: 0)
: "",
dashboardData?.inputBars?.toLocaleString(),
dashboardData?.totalInputBarsWight?.toLocaleString(),
dashboardData?.totalInputBarsPercent
? "%" + dashboardData?.totalInputBarsPercent?.toLocaleString()
: "",
dashboardData?.outputBars?.toLocaleString(),
dashboardData?.totalOutputBarsWight?.toLocaleString(),
dashboardData?.totalOutputBarsPercent
? "%" + dashboardData?.totalOutputBarsPercent?.toLocaleString()
: "",
dashboardData?.lastUpdate &&
formatTime(dashboardData?.lastUpdate),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Autocomplete
sx={{ minWidth: "220px" }}
size="small"
disablePortal
id="hatching"
options={getProvinceList().map((i) => {
return {
label: i.name,
};
})}
onChange={(event, value) => {
if (value.label !== "همه") {
setSelectedProvince(value.label);
} else {
setSelectedProvince("");
}
}}
renderInput={(params) => (
<TextField {...params} label="انتخاب استان" />
)}
/>
<Select
size="small"
labelId="demo-simple-select-label"
id="race"
label="نژاد مرغ"
value={selectedRole}
onChange={(e) => {
setSelectedRole(e.target.value);
}}
>
<MenuItem value={"KillHouse"}>کشتارگاه</MenuItem>
<MenuItem value={"Steward"}>مباشر</MenuItem>
</Select>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "5px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
alignItems="center"
>
<Checkbox
icon={<ToggleOffOutlinedIcon />}
checkedIcon={<ToggleOnIcon />}
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
size="large"
/>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
<Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `https://rsibackend.rasadyar.com/app/transport-carcass-detail-excel/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&role=${selectedRole}&province=${selectedProvince}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"نقش",
"نام واحد",
"شناسه یکتا",
"استان",
"شهرستان",
"محصول",
"تعداد خرید داخل استان",
"وزن خرید داخل استان",
"تعداد خرید خارج استان",
"وزن خرید خارج استان",
"وزن کل انبار",
"وزن کل توزیع",
"درصد توزیع نسبت به انبار",
"تعداد توزیع داخل استان",
"وزن توزیع داخل استان",
"درصد توزیع داخل استان",
"وزن توزیع خارج استان",
"درصد توزیع خارج استان",
"جزئیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="توزیع / فروش گوشت مرغ"
/>
</Grid>
);
};

View File

@@ -0,0 +1,365 @@
import React, { useContext, useEffect, useState } from "react";
import {
Autocomplete,
Button,
Checkbox,
IconButton,
TextField,
Tooltip,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch } from "react-redux";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { getSamasatProvinces } from "../../../../utils/getSamasatProvinces";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import {
ROUTE_ADMINX_ROUTE_NATIONAL_INFO_SLAUGHTER,
ROUTE_SUPER_ADMIN_ROUTE_NATIONAL_INFO_SLAUGHTER,
} from "../../../../routes/routes";
import { ProvinceStewardChickenDistributionsAndSalesDashboardService } from "../../services/province-chicken-distribution-and-sales-dashboard";
import { formatTime } from "../../../../utils/formatTime";
import ToggleOffOutlinedIcon from "@mui/icons-material/ToggleOffOutlined";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import VisibilityIcon from "@mui/icons-material/Visibility";
export const ProvinceChickenStewardSales = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const [dashboardData, setDashboardData] = useState([]);
const [withDate, setWithDate] = useState(false);
const [selectedProvince, setSelectedProvince] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
let response;
getDashboardData();
dispatch(LOADING_START());
response = await axios.get(
`https://rsibackend.rasadyar.com/app/guilds-transport-carcass-detail/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&province=${selectedProvince}&page=${page}&page_size=${perPage}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
// const updateTable = () => {
// fetchApiData(page !== 0 ? page : 1);
// };
const getDashboardData = () => {
dispatch(
ProvinceStewardChickenDistributionsAndSalesDashboardService({
province: selectedProvince,
date1: withDate ? selectedDate1 : null,
date2: withDate ? selectedDate2 : null,
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.info?.role,
item?.name,
item?.PartIdCode || item?.jihadiCode,
item?.Province || item?.province,
item?.City || item?.city,
"گوشت مرغ تازه",
item?.info?.totalInputBuyBarsCount?.toLocaleString(),
item?.info?.totalInputBuyBarsWight?.toLocaleString(),
item?.info?.totalOutputBuyBarsCount?.toLocaleString(),
item?.info?.totalOutputBuyBarsWight?.toLocaleString(),
item?.info?.totalWareHouse?.toLocaleString(),
item?.info?.totalInputBuyBarsPercent?.toLocaleString(),
item?.info?.totalOutputBuyBarsPercent?.toLocaleString(),
<IconButton
key={i}
color="success"
size="small"
onClick={() => {
window.open(
getRoleFromUrl() === "AdminX"
? `${ROUTE_ADMINX_ROUTE_NATIONAL_INFO_SLAUGHTER}/${item?.jihadiCode}/${item?.name}/Steward`
: `${ROUTE_SUPER_ADMIN_ROUTE_NATIONAL_INFO_SLAUGHTER}/${item?.jihadiCode}/${item?.name}/Steward`,
"_blank"
);
}}
>
<VisibilityIcon />
</IconButton>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [
dispatch,
selectedDate1,
selectedDate2,
perPage,
selectedProvince,
withDate,
]);
const handleSubmit = async (event) => {
event.preventDefault();
getDashboardData();
dispatch(LOADING_START());
try {
const response = await axios.get(
`https://rsibackend.rasadyar.com/app/guilds-transport-carcass-detail/?&province=${selectedProvince}&search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const getProvinceList = () => {
return [{ name: "همه" }, ...getSamasatProvinces()];
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد",
"نقش",
"محصول",
"وزن خرید داخل استان",
"وزن خرید خارج استان",
"وزن کل خرید",
"درصد خرید داخل استان",
"درصد خرید خارج استان",
"آخرین آپدیت",
]}
data={[
[
dashboardData?.totalCountGuild?.toLocaleString(),
dashboardData?.role,
dashboardData?.product,
dashboardData?.totalInputBuyBarsWight?.toLocaleString(),
dashboardData?.totalOutputBuyBarsWight?.toLocaleString(),
dashboardData?.totalWareHouse?.toLocaleString(),
dashboardData?.totalInputBuyBarsPercent?.toLocaleString(),
dashboardData?.totalOutputBuyBarsPercent?.toLocaleString(),
dashboardData?.lastUpdate &&
formatTime(dashboardData?.lastUpdate),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Autocomplete
sx={{ minWidth: "220px" }}
size="small"
disablePortal
id="hatching"
options={getProvinceList().map((i) => {
return {
label: i.name,
};
})}
onChange={(event, value) => {
if (value.label !== "همه") {
setSelectedProvince(value.label);
} else {
setSelectedProvince("");
}
}}
renderInput={(params) => (
<TextField {...params} label="انتخاب استان" />
)}
/>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "5px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
alignItems="center"
>
<Checkbox
icon={<ToggleOffOutlinedIcon />}
checkedIcon={<ToggleOnIcon />}
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
size="large"
/>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
sx={{ width: { xs: "126px", md: "160px" } }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
<Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `https://rsibackend.rasadyar.com/app/guilds-transport-carcass-detail-excel/?search=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&province=${selectedProvince}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"نقش",
"نام واحد",
"شناسه یکتا",
"استان",
"شهرستان",
"محصول",
"تعداد خرید داخل استان",
"وزن خرید داخل استان",
"تعداد خرید خارج استان",
"وزن خرید خارج استان",
"وزن کل خرید",
"درصد خرید داخل استان",
"درصد خرید خارج استان",
"جزئیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="خرید صنوف"
/>
</Grid>
);
};

Some files were not shown because too many files have changed in this diff Show More