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,297 @@
import { Button, Chip, Divider, TextField, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import React, { useEffect, useState } from "react";
import ChartBar from "../../../../components/chart-bar/ChartBar";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { useDispatch, useSelector } from "react-redux";
import { adminGetHatchingByPeriod } from "../../services/admin-get-hatching-by-period";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import moment from "moment";
import { EMPTY_HATCHING } from "../../../../lib/redux/slices/adminSlice";
import { formatJustDate } from "../../../../utils/formatTime";
export const AdminHatchingByPeriod = () => {
const dispatch = useDispatch();
const { hatchingByPeriod } = useSelector((state) => state.adminSlice);
useEffect(() => {
dispatch(EMPTY_HATCHING());
}, []);
useEffect(() => {
if (hatchingByPeriod && Array.isArray(hatchingByPeriod)) {
setHatchingByPeriodData({
labels: hatchingByPeriod.map((data) => formatJustDate(data?.date)),
datasets: [
{
label: "تعداد",
backgroundColor: ["rgba(33, 72, 214, 0.7)"],
data: hatchingByPeriod.map((data) => data?.quantity || 0),
borderRadius: 5,
},
{
label: "تلفات",
backgroundColor: ["rgba(100, 130, 160, 0.7)"],
data: hatchingByPeriod.map((data) => data?.losses || 0),
borderRadius: 5,
},
// {
// type: "line",
// label: "مقدار استاندارد",
// backgroundColor: ["rgba(0, 120, 10, 0.7)"],
// data: [
// 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500,
// 2500,
// ],
// },
],
});
}
}, [hatchingByPeriod?.length]);
const [hatchingByPeriodData, setHatchingByPeriodData] = useState({
datasets: [],
});
const formik = useFormik({
initialValues: {
periodfrom: moment(Date()).format("YYYY-MM-DD"),
periodto: moment(Date()).format("YYYY-MM-DD"),
chickenAge: "5",
},
validationSchema: Yup.object({
periodfrom: Yup.string()
.typeError("لطفا فیلد را به درستی وارد کنید.!")
.required("این فیلد اجباری است!"),
periodto: Yup.string()
.typeError("لطفا فیلد را به درستی وارد کنید.!")
.required("این فیلد اجباری است!"),
chickenAge: Yup.number()
.typeError("لطفا فیلد را به صورت عددی وارد کنید.!")
.required("این فیلد اجباری است!")
.test("len", "سن مرغ را در بازه 45 روزه وارد کنید", (val, context) => {
if (context.originalValue) {
return context.originalValue <= 45 && context.originalValue > 0;
}
}),
}),
});
const chartTooltipData = (data) => {
if (!data || !data.parsed || typeof data.parsed.x === "undefined") {
return "";
}
const index = data.parsed.x;
if (!data?.dataset?.label) {
return "";
}
if (data.dataset.label === "تلفات") {
return "مرغداری صداقت با مقدار 2000 در تاریخ 1400/02/03";
} else if (data.dataset.label === "مقدار استاندارد") {
return `میزان استاندارد جوجه ریزی در روز ${
data?.formattedValue || ""
} عدد میباشد`;
} else {
const hatchingItem = hatchingByPeriod?.[index];
if (!hatchingItem || !Array.isArray(hatchingItem?.poultry)) {
return "";
}
return hatchingItem.poultry
.map(
(poultryData, i) =>
i +
1 +
"- مرغداری " +
(poultryData?.unitname || "") +
" از استان " +
(poultryData?.province || "") +
" شهر " +
(poultryData?.city || "")
)
.join(", ");
}
};
const chartTooltipTitle = (data) => {
if (!data?.dataset?.label) {
return "";
}
if (data.dataset.label === "تلفات") {
return "تلفات برای این مرغداری ها ثبت شده است";
} else if (data.dataset.label === "مقدار استاندارد") {
return "لاین کنترلی";
} else {
return "جوجه ریزی ها توسط این مرغداری ها ثبت شده است";
}
};
const [dataObject, setDataObject] = useState({
date1: "",
date2: "",
age: "",
});
useEffect(() => {
setDataObject({
date1: formik.values.periodfrom,
date2: formik.values.periodto,
age: formik.values.chickenAge,
});
}, [formik.values]);
const options = {
plugins: {
tooltip: {
callbacks: {
title: (data) => {
if (!data || !Array.isArray(data) || !data[0]) {
return "";
}
return chartTooltipTitle(data[0]);
},
label: (data) => {
if (!data) {
return "";
}
return chartTooltipData(data);
},
},
},
},
};
useEffect(() => {
formik.validateForm();
}, []);
const isFormvalid = (formik) => {
const date =
(new Date(formik.values.periodto) - new Date(formik.values.periodfrom)) /
(1000 * 60 * 60 * 24);
if (date > 0 && date <= 30) {
return false;
} else {
return true;
}
};
return (
<Grid
sx={{ backgroundColor: "#fff2fd", borderRadius: "25px" }}
mt={SPACING.SMALL}
xs={12}
container
justifyContent="center"
>
<Grid xs={10} lg={10} mb={SPACING.MEDIUM} mt={SPACING.SMALL}>
<Divider mb={SPACING.SMALL}>
<Chip label="پیش بینی جوجه ریزی" />
</Divider>
<Grid
xs={12}
sm={12}
md={12}
lg={8}
display="flex"
mt={SPACING.SMALL}
mb={SPACING.SMALL}
gap={SPACING.SMALL}
alignItems="center"
>
<Typography sx={{ width: "100%" }} color="primary" variant="body2">
دریافت بر اساس بازه دلخواه :
</Typography>
<DatePicker
label="بازه از"
id="periodfrom"
renderInput={(params) => <TextField size="small" {...params} />}
value={formik.values.periodfrom}
error={
formik.touched.periodfrom
? Boolean(formik.errors.periodfrom)
: null
}
onChange={(e) => {
formik.setFieldValue(
"periodfrom",
moment(e).format("YYYY-MM-DD")
);
}}
onBlur={formik.handleBlur}
helperText={
formik.touched.periodfrom && Boolean(formik.errors.periodfrom)
? formik.errors.periodfrom
: null
}
/>
<DatePicker
label="تا"
id="periodto"
renderInput={(params) => <TextField size="small" {...params} />}
value={formik.values.periodto}
error={
formik.touched.periodto ? Boolean(formik.errors.periodto) : null
}
onChange={(e) => {
formik.setFieldValue("periodto", moment(e).format("YYYY-MM-DD"));
}}
onBlur={formik.handleBlur}
helperText={
formik.touched.periodto && Boolean(formik.errors.periodto)
? formik.errors.periodto
: null
}
/>
<TextField
id="chickenAge"
size="small"
label="سن جوجه"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.chickenAge}
error={
formik.touched.chickenAge
? Boolean(formik.errors.chickenAge)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.chickenAge && Boolean(formik.errors.chickenAge)
? formik.errors.chickenAge
: null
}
/>
<Button
disabled={isFormvalid(formik)}
sx={{ width: "100%" }}
size="small"
variant="contained"
onClick={() => {
dispatch(LOADING_START());
dispatch(adminGetHatchingByPeriod(dataObject)).then(() => {
dispatch(LOADING_END());
});
}}
>
ارسال اطلاعات
</Button>
</Grid>
<ChartBar options={options} chartData={hatchingByPeriodData} />
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,296 @@
import { Button, IconButton, Tooltip } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useFormik } from "formik";
import moment from "moment";
import { useState } from "react";
import DeleteIcon from "@mui/icons-material/Delete";
import { useContext } from "react";
import Tour from "reactour";
import { AvicultureNewHatching } from "../../../aviculture/components/aviculture-new-hatching/AvicultureNewHatching";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { avicultureGetHatchings } from "../../../aviculture/services/aviculture-get-hatchings";
import { Yup } from "../../../../lib/yup/yup";
import { avicultureDeleteHatching } from "../../../aviculture/services/aviculture-delete-hatching";
import { formatJustDate } from "../../../../utils/formatTime";
const steps = [
{
selector: ".first-step",
content: () => <div>برای ثبت جوجه ریزی اینجا کلیک کنید!</div>,
},
{
selector: ".second",
content: () => (
<div>در این قسمت جوجه ریزی های ثبت شده توسط شما نمایش داده می شود.</div>
),
},
];
export const AdminHatching = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const { avicultureHatchings } = useSelector((state) => state.avicultureSlice);
const [isTourOpen, setIsTourOpen] = useState(false);
const [dataTable, setDataTable] = useState([]);
const [dataTableArchived, setDataTableArchuved] = useState([]);
useEffect(() => {
dispatch(avicultureGetHatchings());
}, []);
useEffect(() => {
const filteredData = avicultureHatchings?.filter(
(item) => item.allowHatching === "pending"
);
const filteredDataArchived = avicultureHatchings?.filter(
(item) => item.allowHatching === "True"
);
const d = filteredData?.map((item, i) => {
const quantity = item?.quantity || 0;
const losses = item?.losses || 0;
const leftOver = item?.leftOver || 0;
const killedNumber = quantity - losses - leftOver;
const percentage =
quantity > 0
? (value) => ((value * 100) / quantity).toFixed(0)
: () => "0";
return [
i + 1,
item?.poultry?.unitName || "",
item?.hall || "",
item?.period || "",
formatJustDate(item?.createDate),
formatJustDate(item?.date),
item?.chickenBreed || "",
item?.age || "",
quantity,
`${losses} (%${percentage(losses)})`,
killedNumber + ` (%${percentage(killedNumber)})`,
`${leftOver} (%${percentage(leftOver)})`,
<Tooltip key={item?.key} title={"حذف جوجه ریزی"} placement="left-start">
<IconButton
aria-label="delete"
color="error"
onClick={() => {
dispatch(LOADING_START());
dispatch(avicultureDeleteHatching(item?.key)).then((r) => {
dispatch(LOADING_END());
if (r?.error) {
const errorMessage = r.error?.message || "";
if (errorMessage.includes("403")) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "برای این جوجه ریزی درخواست کشتار ثبت شده است!",
severity: "error",
});
} else if (errorMessage.includes("400")) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "برای این جوجه ریزی بازرسی ثبت شده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
}
} else {
dispatch(avicultureGetHatchings());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>,
];
});
setDataTable(d);
const b = filteredDataArchived?.map((item, i) => {
const quantity = item?.quantity || 0;
const losses = item?.losses || 0;
const leftOver = item?.leftOver || 0;
const killedNumber = quantity - losses - leftOver;
const percentage =
quantity > 0
? (value) => ((value * 100) / quantity).toFixed(0)
: () => "0";
return [
i + 1,
item?.poultry?.unitName || "",
item?.hall || "",
item?.period || "",
formatJustDate(item?.createDate),
formatJustDate(item?.date),
item?.chickenBreed || "",
item?.age || "",
quantity,
`${losses} (%${percentage(losses)})`,
killedNumber + ` (%${percentage(killedNumber)})`,
`${leftOver} (%${percentage(leftOver)})`,
];
});
setDataTableArchuved(b);
}, [avicultureHatchings]);
const formik = useFormik({
initialValues: {
noChicken: "",
slaughterDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
race: "آرین",
weight: "",
},
validationSchema: Yup.object({
noChicken: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
weight: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container gap={SPACING.MEDIUM} direction="column" xs={12}>
<Grid
container
gap={SPACING.SMALL}
justifyContent={{ xs: "center", lg: "space-between" }}
alignSelf="center"
alignItems="center"
xs={12}
direction={{ xs: "column", lg: "row" }}
>
<Grid
container
xs={12}
alignItems={"start"}
gap={SPACING.SMALL}
direction={"column"}
>
<Grid
width="100%"
container
alignItems="center"
justifyContent="space-between"
>
<Grid>
<Button
className="first-step"
variant={"contained"}
disabled={true}
onClick={() => {
dispatch(
DRAWER({
title: "ثبت اطلاعات جوجه ریزی",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <AvicultureNewHatching />,
})
);
}}
>
ثبت جوجه ریزی جدید
</Button>
</Grid>
<Grid>
{/* <Button
onClick={() => setIsTourOpen(!isTourOpen)}
variant="outlined"
endIcon={<HelpOutlineIcon />}
>
راهنما
</Button> */}
</Grid>
</Grid>
<Tour
steps={steps}
isOpen={isTourOpen}
onRequestClose={() => setIsTourOpen(false)}
styles={{
popover: (base) => ({
...base,
borderRadius: "10px",
"--reactor-accent": "red",
}),
}}
/>
<Grid width="100%" className="second">
<AdvancedTable
name="دوره های فعال جوجه ریزی"
data={dataTable}
columns={[
"ردیف",
"نام فارم",
"سالن",
"دوره جوجه ریزی",
"تاریخ ثبت جوجه ریزی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"تلفات دوره",
"کشتار شده",
"مانده برای کشتار",
"اقدام",
]}
/>
</Grid>
<Grid mt={SPACING.SMALL} width="100%" className="second">
<AdvancedTable
name="بایگانی جوجه ریزی"
data={dataTableArchived}
columns={[
"ردیف",
"نام فارم",
"سالن",
"دوره جوجه ریزی",
"تاریخ ثبت جوجه ریزی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"تلفات دوره",
"کشتار شده",
"مانده در سالن",
]}
/>
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,247 @@
import {
Card,
CardActionArea,
CardContent,
CardMedia,
Typography,
} from "@mui/material";
import React from "react";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { PropTypes } from "prop-types";
import img1 from "../../../../assets/images/document.jpg";
import img2 from "../../../../assets/images/chicken.svg";
import img3 from "../../../../assets/images/users.jpg";
import img4 from "../../../../assets/images/price.jpg";
export default function AdminStaticInfoInBox({ infoData }) {
return (
<Grid xs={12} sm={8} md={8} lg={12}>
<Grid
container
mt={SPACING.MEDIUM}
gap={SPACING.SMALL}
justifyContent="space-around"
>
<Card>
<CardActionArea>
<CardMedia
component="img"
height="240"
image={img1}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="body1">
پرونده ها
</Typography>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
فعال:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.numberOfActiveFiles}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
رد شده:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.numberOfRejectedFiles}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
بایگانی شده:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.numberOfArchiveFiles}
</Typography>
</Grid>
</CardContent>
</CardActionArea>
</Card>
<Card>
<CardActionArea>
<CardMedia
component="img"
height="240"
image={img2}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="body1">
جوجه ریزی
</Typography>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
جوجه ریزی:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.realHatching}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
تلفات:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.totalLossess}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
مجموع:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.totalHatching}
</Typography>
</Grid>
</CardContent>
</CardActionArea>
</Card>
<Card>
<CardActionArea>
<CardMedia
component="img"
height="240"
image={img3}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="body1">
کاربران
</Typography>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
کل:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.totalUsers}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
کاربر:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.numberOfUsers}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
اپراتور:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.numberOfOperators}
</Typography>
</Grid>
</CardContent>
</CardActionArea>
</Card>
<Card>
<CardActionArea>
<CardMedia
component="img"
height="240"
image={img4}
alt="green iguana"
/>
<CardContent>
<Typography gutterBottom variant="body1">
نرخ مرغ
</Typography>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
قیمت روز:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.price} ریال
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
کف قیمت:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.floorPrice} ریال
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
سقف قیمت:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{infoData?.cellingPrice} ریال
</Typography>
</Grid>
</CardContent>
</CardActionArea>
</Card>
</Grid>
</Grid>
);
}
AdminStaticInfoInBox.propTypes = {
infoData: PropTypes.any,
};

View File

@@ -0,0 +1,80 @@
import { useLocation } from "react-router-dom";
import { Button } from "@mui/material";
import {
ROUTE_ADMIN_ARCHIVED_REQUESTS,
ROUTE_ADMIN_AWAITING_INSPECTION_REQUESTS,
ROUTE_ADMIN_AWAITING_PAYMENT_REQUESTS,
ROUTE_ADMIN_HATCHING,
ROUTE_ADMIN_REJECTED_REQUESTS,
ROUTE_ADMIN_REQUESTS,
} from "../../../../routes/routes";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
export const AdminRequestsOperations = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
>
<NavLink
to={ROUTE_ADMIN_HATCHING}
active={pathname === ROUTE_ADMIN_HATCHING ? "true" : null}
>
<Button variant="text" color="inherit">
ثبت اطلاعات جوجه ریزی
</Button>
</NavLink>
<NavLink
to={ROUTE_ADMIN_REQUESTS}
active={pathname === ROUTE_ADMIN_REQUESTS ? "true" : null}
>
<Button variant="text" color="inherit">
ثبت درخواست کشتار
</Button>
</NavLink>
<NavLink
to={ROUTE_ADMIN_AWAITING_PAYMENT_REQUESTS}
active={
pathname === ROUTE_ADMIN_AWAITING_PAYMENT_REQUESTS ? "true" : null
}
>
<Button variant="text" color="inherit">
در انتظار پرداخت
</Button>
</NavLink>
<NavLink
to={ROUTE_ADMIN_AWAITING_INSPECTION_REQUESTS}
active={
pathname === ROUTE_ADMIN_AWAITING_INSPECTION_REQUESTS ? "true" : null
}
>
<Button variant="text" color="inherit">
در انتظار بازرسی
</Button>
</NavLink>
<NavLink
to={ROUTE_ADMIN_REJECTED_REQUESTS}
active={pathname === ROUTE_ADMIN_REJECTED_REQUESTS ? "true" : null}
>
<Button variant="text" color="inherit">
سفارشات رد شده
</Button>
</NavLink>
<NavLink
to={ROUTE_ADMIN_ARCHIVED_REQUESTS}
active={pathname === ROUTE_ADMIN_ARCHIVED_REQUESTS ? "true" : null}
>
<Button variant="text" color="inherit">
سفارشات بایگانی شده
</Button>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,7 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const adminGetCharts = createAsyncThunk("ADMIN_GET_CHARTS", async () => {
const { data, status } = await axios.get("chart");
return { data, status };
});

View File

@@ -0,0 +1,17 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const adminGetHatchingByPeriod = createAsyncThunk(
"ADMIN_GET_HATCHING_BY_PERIOD",
async (dataObject) => {
const { data, status } = await axios.get(
"forcast_hatching/?date1=" +
dataObject.date1 +
"&date2=" +
dataObject.date2 +
"&day=" +
dataObject.age
);
return { data, status };
}
);

View File

@@ -0,0 +1,222 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { reportsList } from "./reportList";
import { AppContext } from "../../../../contexts/AppContext";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import { Button, IconButton, TextField, Typography } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { useDispatch, useSelector } from "react-redux";
import { adminXGetReportStatusService } from "../../services/admin-x-get-report-status-service";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ManageSearchIcon from "@mui/icons-material/ManageSearch";
export const AdminCExcelStatus = () => {
const [tableData, setTableData] = useState([]);
const userKey = useSelector((state) => state.userSlice?.userProfile?.key);
const authToken = useSelector((state) => state.userSlice?.authToken);
const [loadingIndex, setLoadingIndex] = useState(null);
const [allReports, setAllReports] = useState([]);
const [update, setUpdate] = useState(false);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, [setSelectedDate1, setSelectedDate2]);
useEffect(() => {
const newData = reportsList.map((report) => ({
...report,
status: "",
}));
setAllReports(newData);
}, []);
function replaceUrlParams(url, date1, date2, role, key, token) {
if (!url || typeof url !== "string") {
return url || "";
}
const [baseUrl, queryString] = url.split("?");
if (!queryString) {
return url;
}
const params = new URLSearchParams(queryString);
if (params.has("date1")) {
params.set("date1", date1);
}
if (params.has("date2")) {
params.set("date2", date2);
}
if (params.has("start")) {
params.set("start", date1);
}
if (params.has("end")) {
params.set("end", date2);
}
if (params.has("role")) {
params.set("role", role);
}
if (params.has("token")) {
params.set("token", token);
}
if (params.has("key")) {
params.set("key", key);
}
return `${baseUrl}?${params.toString()}`;
}
const handleCheckAll = async () => {
const updatedData = [...allReports];
for (let i = 0; i < allReports.length; i++) {
setLoadingIndex(i);
try {
const response = await dispatch(
adminXGetReportStatusService({
url: replaceUrlParams(
allReports[i]?.url,
selectedDate1,
selectedDate2,
getRoleFromUrl(),
userKey,
authToken
),
})
);
if (response?.payload?.data?.status) {
updatedData[i].status = response.payload.data.status;
}
} catch (error) {
console.error("Error fetching report status:", error);
}
}
setLoadingIndex(null);
setAllReports(updatedData);
setUpdate((prevUpdate) => !prevUpdate);
};
const handleSingleCheck = async (index) => {
setLoadingIndex(index);
try {
const response = await dispatch(
adminXGetReportStatusService({
url: replaceUrlParams(
allReports[index]?.url,
selectedDate1,
selectedDate2,
getRoleFromUrl(),
userKey,
authToken
),
})
);
const newData = [...allReports];
if (response?.payload?.data?.status) {
newData[index].status = response.payload.data.status;
}
setAllReports(newData);
} catch (error) {
console.error("Error fetching report status:", error);
} finally {
setLoadingIndex(null);
}
};
useEffect(() => {
const d = allReports.map((item, i) => [
i + 1,
item?.title || "",
item?.url ? item.url.split("/")[0] : "",
<IconButton
color="primary"
key={`button-${i}`}
onClick={() => handleSingleCheck(i)}
disabled={loadingIndex === i}
>
<ManageSearchIcon />
</IconButton>,
item?.status ? (
<Typography
key={`status-${i}`}
style={{
color:
parseInt(item.status) === 200 || item.status === "200"
? "green"
: "red",
fontWeight: "bold",
}}
>
({item.status}){" "}
{parseInt(item.status) === 200 || item.status === "200"
? "سالم"
: "مشکل دارد"}
</Typography>
) : (
"-"
),
]);
setTableData(d);
}, [allReports, loadingIndex, update]);
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Grid container gap={SPACING.SMALL} alignItems="center">
<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>
<Button variant="contained" onClick={handleCheckAll}>
تست همگانی
</Button>
</Grid>
</Grid>
<ResponsiveTable
title="بررسی اکسل"
noPagination
data={tableData}
columns={["ردیف", "عنوان", "آدرس", "بررسی", "وضعیت"]}
/>
</Grid>
);
};

View File

@@ -0,0 +1,219 @@
export const reportsList = [
{
title: "مدیریت بار",
url: "bar_excel/?start=2024-08-03&end=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator&search=filter&value=",
},
{
title: "گزارش جزییات کشتار",
url: "detail_of_killing_excel/?date1=2024-08-03&date2=2024-08-03&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "گزارش جامع کشتارگاه",
url: "comprehensive_report_of_the_slaughterhouse_excel/?date1=2024-08-03&date2=2024-08-03",
},
{
title: "پایش کلی بارها",
url: "monitor_loads_excel/?role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&date1=2024-08-03&date2=2024-08-03",
},
{
title: "مدیریت بارهای تکمیل نشده",
url: "bar_excel/?start=2024-05-21&end=2024-08-03&role=ProvinceOperator&state=bar_pending&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=",
},
{
title: "مدیریت بارهای تکمیل شده",
url: "bar_excel/?start=2024-07-22&end=2024-08-03&state=completed&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=",
},
{
title: "بارهای حذف شده",
url: "bar_excel_trash_true/?start=2024-08-03&end=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator&search=filter&value=",
},
{
title: "فروش خارج از استان",
url: "bar_free_excel/?date1=2024-08-03&date2=2024-08-03&state=accepted&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "مدیریت بارهای زنجیره",
url: "bar_chain_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator&search=filter&value=&state=accepted&date1=2024-08-03&date2=2024-08-03",
},
{
title: "خرید خارج از استان زنده",
url: "kill_house_free_bar_excel/?role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&date1=2024-08-03&date2=2024-08-03&type=live",
},
{
title: "خرید خارج از استان لاشه",
url: "kill_house_free_bar_excel/?role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&date1=2024-08-03&date2=2024-08-03&type=carcass",
},
{
title: "درخواست مرغدار",
url: "poultry_kill_request_excel/?start=2024-08-03&end=2024-08-03&role=ProvinceOperator&token=vamptUktoL9b1htBgvjE14XTZg7Bg4",
},
{
title: "درخواست کشتارگاه",
url: "kill_house_excel/?start=2024-08-03&end=2024-08-03",
},
{
title: "جوجه ریزی",
url: "0/hatching_excel/?role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=",
},
{
title: "بایگانی جوجه ریزی",
url: "archive_hatching_excel/?search=filter&value=&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator",
},
{
title: "جوجه ریزی در بازه تاریخی",
url: "hatching_date_range_excel/?date1=2024-08-03&date2=2024-08-03",
},
{
title: "وضعیت پرونده",
url: "poultry_request_report_excel/?start=2024-08-03&end=2024-08-03&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "گزارش مغایرت اطلاعات کشتار و عدم فعالیت نقش ها",
url: "discrepancy_report_excel/?date1=2024-08-02&date2=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator",
},
{
title:
"گزارش کلی فارم های فعال مرغ گوشتی دارای مانده در سالن بیشتر از 10 درصد و بازه سنی 55 تا 90 روزه",
url: "poultry_hatching_between_50age_70age_excel/",
},
{
title: "گزارش کشتار روزانه",
url: "daily_process_klling_proccess_excel/?date=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "گزارش پخش روزانه",
url: "daily_process_excel/?date=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "تراکنش های موفق",
url: "successful_transactions_excel/?date1=2024-08-03&date2=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "تراکنش های ناموفق",
url: "unsuccessful_transactions_excel/?date1=2024-08-03&date2=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "مدیریت اصناف",
url: "guilds_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=&role=ProvinceOperator",
},
{
title: "مدیریت مباشرین",
url: "stewards_excel/",
},
{
title: "خودروها",
url: "car_province_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=Province",
},
{
title: "مدیریت کاربران",
url: "get_all_user_excel/",
},
{
title: "مدیریت مرغداران",
url: "management_poultry/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=ProvinceOperator",
},
{
title: "مدیریت خریداران",
url: "kill_house_user_excel/",
},
{
title: "گزارش پخش لاشه مرغ گرم",
url: "all_inventory_excel/?date1=2024-07-22&date2=2024-08-03",
},
{
title: "گزارش پخش لاشه مرغ گرم از مباشر به صنف",
url: "steward_ware_house_total_report_daily_excel/?date1=2024-06-21&date2=2024-08-03",
},
{
title: "کارمزد پرداخت شده کشتارگاه",
url: "kill_house_total_transactions_wage_payid_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=&role=ProvinceOperator",
},
{
title: "پرداختی زنجیره ها",
url: "chain-company-total-transactions_not_payid_excel/?token=vamptUktoL9b1htBgvjE14XTZg7Bg4",
},
{
title: "ریز تراکنش ها",
url: "payment_transactions_province_excel/?role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "اطلاعات تعرفه بارهای روزانه",
url: "kill_house_total_wage_excel/?token=vamptUktoL9b1htBgvjE14XTZg7Bg4&date1=2024-08-03&date2=2024-08-03",
},
{
title: "پایش تعرفه برای استان",
url: "kill_house_total_transactions_wage_payid_super_admin_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=AdminX",
},
{
title: "پایش تعرفه برای ادمین ایکس",
url: "kill_house_total_transactions_wage_payid_admin_x_excel/?role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "خرید مستقیم",
url: "direct_purchase_excel/?date1=2024-08-03&date2=2024-08-03&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "بایگانی خرید مستقیم",
url: "direct_purchase_archive_excel/?date1=2024-08-03&date2=2024-08-03&role=ProvinceOperator&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "بایگانی صادرات",
url: "export_kill_house_excel/?date1=2024-08-03&date2=2024-08-03&role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "گزارش کلی بار خارج از استان",
url: "general_free_bar_excel/?date1=2024-08-03&date2=2024-08-03&role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=",
},
{
title: "بار خارج از استان",
url: "bar_free_excel/?date1=2024-08-03&date2=2024-08-03&state=pending&role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "بارهای تایید شده خارج از استان",
url: "bar_free_excel/?date1=2024-08-03&date2=2024-08-03&state=accepted&role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "بارهای رد شده خارج از استان",
url: "bar_free_excel/?date1=2024-08-03&date2=2024-08-03&state=rejected&role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "خریداران خارج از استان",
url: "out_province_poultry_request_buyers_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=AdminX&search=filter&value=",
},
{
title: "جوجه ریزی زنجیره",
url: "0/hatching_excel/?role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&chain=true",
},
{
title: "مدیریت بار زنجیره ها",
url: "bar_chain_excel/?key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&role=AdminX&search=filter&value=&state=accepted&date1=2024-08-03&date2=2024-08-03",
},
{
title: "شرکت زنجیره",
url: "chain_company_buyers_excel/?role=AdminX&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904&search=filter&value=",
},
{
title: "جوجه ریزی بایگانی شرکت زنجیره",
url: "archive_hatching_excel/?chain=true",
},
{
title: "مدیریت تخصیصات",
url: "allocated_excel/?start=2024-08-03&end=2024-08-03",
},
{
title: "گزارش کشتار روزانه",
url: "daily_process_klling_proccess_excel/?date=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "گزارش پخش روزانه",
url: "daily_process_excel/?date=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "تراکنش های موفق",
url: "successful_transactions_excel/?date1=2024-08-03&date2=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
{
title: "تراکنش های ناموفق",
url: "unsuccessful_transactions_excel/?date1=2024-08-03&date2=2024-08-03&key=fde6fff6-7f4c-4e10-5604-0d45f8b41904",
},
];

View File

@@ -0,0 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const adminXGetReportStatusService = createAsyncThunk(
"ADMINX_REPORT_STATUS",
async (d, { dispatch }) => {
const { data, status } = await axios.get("check_excel/", {
params: { url: d.url },
});
return { data, status };
}
);

View File

@@ -0,0 +1,111 @@
import { Button } from "@mui/material";
// import moment from "moment";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
// import { Timer } from "../../../../components/timer/Timer";
import { SPACING } from "../../../../data/spacing";
import { ROUTE_SLAUGHTER_FILE } from "../../../../routes/routes";
import { formatTime } from "../../../../utils/formatTime";
import { auctionSlaughterRequests } from "../../services/auction-slaughter-requests";
export const AuctionsBids = () => {
const [tableRows, setTableRows] = useState([]);
const dispatch = useDispatch();
const navigate = useNavigate();
const { auctionSlaughterRequestsData } = useSelector(
(state) => state.auctionSlice
);
useEffect(() => {
dispatch(auctionSlaughterRequests());
}, []);
useEffect(() => {
if (
!auctionSlaughterRequestsData ||
!Array.isArray(auctionSlaughterRequestsData)
) {
setTableRows([]);
return;
}
setTableRows(
auctionSlaughterRequestsData.map((item) => {
// const auctionFinishDate = moment(new Date(item.date));
// const currentDate = moment();
// const diff = auctionFinishDate.diff(currentDate);
// let auctionRemainedSeconds = moment.duration(diff).asSeconds();
const auctionState =
item?.state === "accepted"
? "برنده شده اید!"
: item?.state === "pending"
? "در حال انجام مزایده"
: "درخواست شما پذیرفته نشد.";
return [
item?.poultryRequest?.id || "",
item?.poultryRequest?.orderCode || "",
(item?.fee || 0) + " ﷼",
(item?.poultryRequest?.quantity || 0) + " قطعه",
item?.poultryRequest?.hatching?.date || "",
(item?.poultryRequest?.chickenBreed || "") +
(item?.poultryRequest?.chickenBreed &&
item?.poultryRequest?.process?.poultry?.age
? " - "
: "") +
(item?.poultryRequest?.process?.poultry?.age || ""),
(item?.poultryRequest?.IndexWeight || 0) + " کیلوگرم",
item?.poultryRequest?.poultry?.address?.province?.name || "",
item?.poultryRequest?.poultry?.address?.city?.name || "",
item?.date ? formatTime(item.date) : "",
auctionState === "برنده شده اید!" ? (
<Grid container alignItems="center" justifyContent="center">
{auctionState}
<Button
onClick={() => {
navigate(
ROUTE_SLAUGHTER_FILE + (item?.poultryRequest?.id || "")
);
}}
>
ادامه خرید
</Button>
</Grid>
) : (
auctionState
),
];
})
);
}, [auctionSlaughterRequestsData]);
return (
<>
{auctionSlaughterRequestsData &&
Array.isArray(auctionSlaughterRequestsData) &&
auctionSlaughterRequestsData.length > 0 && (
<Grid container gap={SPACING.SMALL} p={SPACING.SMALL} width="100%">
<AdvancedTable
name="مزایده های شما"
columns={[
"شماره مزایده",
"کدسفارش",
"قیمت پیشنهادی شما",
"تعداد",
"تاریخ جوجه ریزی",
"نژاد و سن به روز",
"وزن",
"استان",
"شهرستان",
"تاریخ ثبت پیشنهاد",
"وضعیت",
]}
data={tableRows}
/>
</Grid>
)}
</>
);
};

View File

@@ -0,0 +1,221 @@
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import {
Button,
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
Slider,
} from "@mui/material";
import { useState } from "react";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { useDispatch, useSelector } from "react-redux";
import { PropTypes } from "prop-types";
import {
auctionFilterByAge,
auctionFilterByPrice,
auctionFilterByQuantity,
auctionFilterByRace,
auctionFilterByWeight,
} from "../../../../lib/redux/slices/auctionSlice";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useEffect } from "react";
import { avicultureGetChickenPrice } from "../../../aviculture/services/aviculture-get-chicken-price";
export const AuctionFilterRequestAdvance = ({
minQuantity = 0,
maxQuantity = 1000,
}) => {
const dispatch = useDispatch();
const { avicultureChickenPrice } = useSelector(
(state) => state.avicultureSlice
);
useEffect(() => {
dispatch(LOADING_START());
dispatch(avicultureGetChickenPrice());
dispatch(LOADING_END());
}, []);
const formik = useFormik({
initialValues: {
age: "",
race: "همه",
},
validationSchema: Yup.object({
age: Yup.number().typeError("لطفا سن مرغ را وارد کنید."),
}),
});
const [number, setNumber] = useState([minQuantity || 0, maxQuantity || 1000]);
const [weight, setWeight] = useState([1, 5]);
const [age, setAge] = useState([30, 70]);
const [price, setPrice] = useState([
avicultureChickenPrice?.floorPrice || 0,
avicultureChickenPrice?.ceilingPrice || 100000,
]);
useEffect(() => {
if (
avicultureChickenPrice?.floorPrice &&
avicultureChickenPrice?.ceilingPrice
) {
setPrice([
avicultureChickenPrice.floorPrice,
avicultureChickenPrice.ceilingPrice,
]);
}
}, [avicultureChickenPrice]);
const handleChangeNumber = (event, newNumber) => {
setNumber(newNumber);
};
const handleChangeWeight = (event, newWeight) => {
setWeight(newWeight);
};
const handleChangeAge = (event, newAge) => {
setAge(newAge);
};
const handleChangePrice = (event, newPrice) => {
setPrice(newPrice);
};
return (
<Grid
container
p={SPACING.MEDIUM}
direction="column"
flex="1"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">نژاد مرغ</InputLabel>
<Select
labelId="demo-simple-select-label"
id="race"
label="نژاد مرغ"
value={formik.values.race}
error={formik.touched.race ? Boolean(formik.errors.race) : null}
onChange={(e) => {
formik.setFieldValue("race", e.target.value);
}}
onBlur={formik.handleBlur}
>
<MenuItem value={"همه"}>همه</MenuItem>
<MenuItem value={"آرین"}>آرین</MenuItem>
<MenuItem value={"راس"}>راس</MenuItem>
<MenuItem value={"آربراکرز (آپلاس)"}>آربراکرز (آپلاس)</MenuItem>
<MenuItem value={"کاب"}>کاب</MenuItem>
<MenuItem value={"هوبارد"}>هوبارد</MenuItem>
</Select>
<FormHelperText>
{formik.touched.race && Boolean(formik.errors.race)
? formik.errors.race
: null}
</FormHelperText>
</FormControl>
</Grid>
{number &&
Array.isArray(number) &&
number[0] !== undefined &&
number[0] !== Infinity &&
minQuantity !== undefined &&
maxQuantity !== undefined ? (
<Grid display="flex" gap={SPACING.SMALL}>
تعداد:
<Slider
size="small"
getAriaLabel={() => "تعداد"}
value={number}
min={minQuantity || 0}
max={maxQuantity || 1000}
step={100}
onChange={handleChangeNumber}
valueLabelDisplay="auto"
/>
</Grid>
) : (
""
)}
<Grid display="flex" gap={SPACING.SMALL}>
سن:
<Slider
size="small"
value={age}
min={30}
max={70}
onChange={handleChangeAge}
valueLabelDisplay="auto"
/>
</Grid>
<Grid display="flex" gap={SPACING.SMALL}>
وزن:
<Slider
size="small"
getAriaLabel={() => "Temperature range"}
value={weight}
min={1}
max={5}
onChange={handleChangeWeight}
valueLabelDisplay="auto"
/>
</Grid>
<Grid display="flex" gap={SPACING.SMALL}>
قیمت:
<Slider
size="small"
getAriaLabel={() => "سقف و کف قیمت امروز"}
value={price}
step={5000}
min={avicultureChickenPrice?.floorPrice || 0}
max={avicultureChickenPrice?.ceilingPrice || 100000}
onChange={handleChangePrice}
valueLabelDisplay="auto"
/>
</Grid>
</Grid>
<Grid>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(auctionFilterByQuantity(number));
dispatch(auctionFilterByAge(age));
dispatch(
auctionFilterByRace(
formik.values.race === "همه" ? "" : formik.values.race
)
);
dispatch(auctionFilterByWeight(weight));
dispatch(auctionFilterByPrice(price));
dispatch(DRAWER({ right: false, bottom: false, content: null }));
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
);
};
AuctionFilterRequestAdvance.propTypes = {
minQuantity: PropTypes.number,
maxQuantity: PropTypes.number,
};

View File

@@ -0,0 +1,173 @@
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import {
Button,
Checkbox,
FormControl,
FormControlLabel,
InputLabel,
ListItemText,
MenuItem,
OutlinedInput,
Radio,
RadioGroup,
Select,
Typography,
} from "@mui/material";
import { useSelector } from "react-redux";
import { useEffect, useState } from "react";
import { format } from "date-fns-jalali";
import { useDispatch } from "react-redux";
import { auctionFilterByDate } from "../../../../lib/redux/slices/auctionSlice";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
export const AuctionFilterRequestDate = () => {
const dispatch = useDispatch();
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const { auctions } = useSelector((state) => state.auctionSlice || {});
const [selectedDates, setSelectedDates] = useState([]);
const [availableDates, setAvailableDates] = useState([]);
const [value, setValue] = useState("all");
const handleChangeRadioButton = (event) => {
setValue(event.target.value);
};
const handleChange = (event) => {
const {
target: { value },
} = event;
setSelectedDates(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
useEffect(() => {
if (auctions && Array.isArray(auctions)) {
const data = auctions
.filter((item) => item?.sendDate)
.map((item) => {
try {
return format(new Date(item.sendDate), "yyyy/MM/dd");
} catch (error) {
console.error("Error formatting date:", error);
return null;
}
})
.filter((date) => date !== null);
setAvailableDates(data);
} else {
setAvailableDates([]);
}
}, [auctions]);
return (
<Grid
container
p={SPACING.MEDIUM}
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<FormControl>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChangeRadioButton}
>
<FormControlLabel
value="all"
control={<Radio />}
label="بدون فیلتر"
/>
<FormControlLabel
value="filter"
control={<Radio />}
label="انتخاب تاریخ"
/>
</RadioGroup>
</FormControl>
</Grid>
{value === "filter" && (
<>
<Grid>
<Typography variant="body2">
تاریخ هایی که مایل به رویت مناقصات آنها هستید را انتخاب کنید.
</Typography>
</Grid>
<Grid>
<FormControl sx={{ width: "100%" }}>
<InputLabel id="demo-multiple-checkbox-label">
تاریخ های انتخاب شده
</InputLabel>
<Select
multiple
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
value={selectedDates}
onChange={handleChange}
input={<OutlinedInput label="تاریخ های انتخاب شده" />}
renderValue={(selected) =>
Array.isArray(selected) ? selected.join(", ") : ""
}
MenuProps={MenuProps}
>
{availableDates.map((date, i) => (
<MenuItem key={(date || "") + i} value={date || ""}>
<Checkbox
checked={
Array.isArray(selectedDates) &&
selectedDates.indexOf(date) > -1
}
/>
<ListItemText primary={date || ""} />
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</>
)}
<Grid>
<Button
fullWidth
variant="contained"
onClick={() => {
if (value === "all") {
dispatch(auctionFilterByDate(null));
} else {
dispatch(
auctionFilterByDate(
Array.isArray(selectedDates) ? selectedDates : []
)
);
}
dispatch(DRAWER({ right: false, bottom: false, content: null }));
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,149 @@
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { Autocomplete, Button, TextField } from "@mui/material";
import { useState } from "react";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch } from "react-redux";
import { cityGetProvinces } from "../../../city/services/CityGetProvinces";
import { useEffect } from "react";
import { cityGetCity } from "../../../city/services/city-get-city";
import {
auctionFilterByCity,
auctionFilterByProvince,
} from "../../../../lib/redux/slices/auctionSlice";
export const AuctionFilterRequestZone = () => {
const [provinceData, setProvinceData] = useState();
const [cityData, setCityData] = useState();
const [provinceKey, setProvinceKey] = useState();
const [selectedProvince, setSelectedProvince] = useState();
const [selectedCity, setSelectedCity] = useState();
const [, setCityKey] = useState();
const [isExistProvince, setIsExistProvince] = useState(true);
const dispatch = useDispatch();
useEffect(() => {
dispatch(LOADING_START());
dispatch(cityGetProvinces())
?.then((r) => {
if (r?.payload?.data) {
setProvinceData(r.payload.data);
}
})
.catch((error) => {
console.error("Error fetching provinces:", error);
})
.finally(() => {
dispatch(LOADING_END());
});
}, []);
useEffect(() => {
if (provinceKey) {
dispatch(LOADING_START());
dispatch(cityGetCity(provinceKey))
.then((r) => {
if (r?.payload?.data) {
setCityData(r.payload.data);
setIsExistProvince(false);
}
})
.catch((error) => {
console.error("Error fetching cities:", error);
})
.finally(() => {
dispatch(LOADING_END());
});
} else {
setCityData([]);
setIsExistProvince(true);
}
}, [provinceKey]);
return (
<Grid
container
p={SPACING.MEDIUM}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<Autocomplete
disablePortal
id="province"
options={
provinceData && Array.isArray(provinceData)
? provinceData
.filter((i) => i?.key && i?.name)
.map((i) => ({ id: i.key, label: i.name }))
: []
}
onChange={(e, value) => {
if (value) {
setProvinceKey(value.id);
setSelectedProvince(value.label);
} else {
setProvinceKey(undefined);
setSelectedProvince(undefined);
}
}}
renderInput={(params) => (
<TextField {...params} label="استان را انتخاب کنید" />
)}
/>
</Grid>
<Grid>
<Autocomplete
disabled={isExistProvince}
disablePortal
id="city"
options={
cityData && Array.isArray(cityData)
? cityData
.filter((i) => i?.key && i?.name)
.map((i) => ({ id: i.key, label: i.name }))
: []
}
onChange={(e, value) => {
if (value) {
setCityKey(value.id);
setSelectedCity(value.label);
} else {
setCityKey(undefined);
setSelectedCity(undefined);
}
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
</Grid>
</Grid>
<Grid>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(auctionFilterByCity(selectedCity));
dispatch(auctionFilterByProvince(selectedProvince));
dispatch(DRAWER({ right: false, bottom: false, content: null }));
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,314 @@
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import {
Button,
Divider,
InputAdornment,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { PropTypes } from "prop-types";
import { Timer } from "../../../../components/timer/Timer";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch } from "react-redux";
import { AuctionOfferPrice } from "../../services/auction-offer-price";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { auctionGetAuctions } from "../../services/auction-get-auctions";
import { auctionSlaughterRequests } from "../../services/auction-slaughter-requests";
export const AuctionOfferPriceForm = ({
data,
floorPrice,
ceilingPrice,
highestBidedPrice,
auctionRemainedSeconds,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
offer: "",
},
validationSchema: Yup.object({
offer: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
}),
});
return (
<Grid
container
gap={SPACING.TINY}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.TINY}>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
مانده تا پایان مناقصه :
</Typography>
<Timer key={"auctiontimer"} seconds={auctionRemainedSeconds} />
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
استان :
</Typography>
<Typography variant="body2">
{data?.poultry?.address?.province?.name || ""}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
شهر :
</Typography>
<Typography variant="body2">
{data?.poultry?.address?.city?.name || ""}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
مرغدار :
</Typography>
<Typography variant="body2">
{data?.poultry?.userprofile?.fullName || ""}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
محل پرورش :
</Typography>
<Typography variant="body2">
{data?.poultry?.address?.address || ""}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
نژاد :
</Typography>
<Typography variant="body2">{data?.chickenBreed || ""}</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
تعداد :
</Typography>
<Typography variant="body2" mr={SPACING.TINY}>
{data?.quantity || 0}
</Typography>
قطعه
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
سن :
</Typography>
<Typography variant="body2">
{data?.process?.poultry?.age || 0} روز
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body2"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
وزن :
</Typography>
<Typography variant="body2">
{data?.IndexWeight || 0} کیلوگرم
</Typography>
</Grid>
<Divider />
<Grid
container
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
قیمت پایه :
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{floorPrice || 0}
</Typography>
ریال
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
بالاترین قیمت پیشنهادی :
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{highestBidedPrice || "پیشنهادی وجود ندارد!"}
</Typography>
{highestBidedPrice === "پیشنهادی وجود ندارد!" || !highestBidedPrice
? ""
: "ریال"}
</Grid>
</Grid>
<Grid mt={SPACING.SMALL}>
<TextField
id="offer"
label="قیمت پیشنهادی"
InputProps={{
endAdornment: (
<InputAdornment position="end">ریال</InputAdornment>
),
}}
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.offer}
error={formik.touched.offer ? Boolean(formik.errors.offer) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.offer && Boolean(formik.errors.offer)
? formik.errors.offer
: null
}
/>
</Grid>
<Grid mt={SPACING.SMALL}>
<Button
disabled={!formik.isValid}
fullWidth
variant="contained"
onClick={() => {
dispatch(LOADING_START());
const offerValue = Number(formik.values.offer) || 0;
const floorPriceNum = Number(floorPrice) || 0;
const ceilingPriceNum = Number(ceilingPrice) || 0;
const highestBidedPriceNum =
highestBidedPrice === "پیشنهادی وجود ندارد!" ||
!highestBidedPrice
? 0
: Number(highestBidedPrice) || 0;
if (
(highestBidedPrice === "پیشنهادی وجود ندارد!" ||
!highestBidedPrice ||
(offerValue - highestBidedPriceNum >= 1000 &&
offerValue > floorPriceNum &&
offerValue <= ceilingPriceNum)) &&
data?.key
) {
dispatch(
AuctionOfferPrice({
key: data.key,
fee: offerValue,
})
)
.then((r) => {
if (r?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(auctionGetAuctions());
dispatch(auctionSlaughterRequests());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
})
.catch((error) => {
console.error("Error submitting offer:", error);
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
})
.finally(() => {
dispatch(LOADING_END());
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "قیمت پیشنهادی باید از بیشترین قیمت پیشنهاد شده بیشتر باشد و از حداکثر قیمت روز کمتر باشد.",
severity: "error",
});
dispatch(LOADING_END());
}
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
</Grid>
);
};
AuctionOfferPriceForm.propTypes = {
data: PropTypes.any,
floorPrice: PropTypes.string,
ceilingPrice: PropTypes.string,
highestBidedPrice: PropTypes.string,
auctionRemainedSeconds: PropTypes.string,
};

View File

@@ -0,0 +1,21 @@
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { Button } from "@mui/material";
export const AuctionOperations = () => {
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
>
<NavLink to={null} active={null}>
<Button variant="text" color="inherit">
مناقصه
</Button>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,314 @@
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 { SPACING } from "../../../../data/spacing";
import { auctionGetAuctions } from "../../services/auction-get-auctions";
import { Button, ButtonGroup, Typography } from "@mui/material";
import { AuctionFilterRequestDate } from "../auction-filter-request-date/AuctionFilterRequestDate";
import { AuctionFilterRequestAdvance } from "../auction-filter-request-advance/AuctionFilterRequestAdvance";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getAuctionItemForTable } from "../../utils/get-auction-item-for-table";
import { format } from "date-fns-jalali";
import { AuctionFilterRequestZone } from "../auction-filter-request-zone/AuctionFilterRequestZone";
import {
auctionFilterByAge,
auctionFilterByCity,
auctionFilterByDate,
auctionFilterByPrice,
auctionFilterByProvince,
auctionFilterByQuantity,
auctionFilterByRace,
auctionFilterByWeight,
} from "../../../../lib/redux/slices/auctionSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const AuctionsView = () => {
const dispatch = useDispatch();
const { auctions } = useSelector((state) => state.auctionSlice || {});
const { filterByDate } = useSelector((state) => state.auctionSlice || {});
const { filterByCity } = useSelector((state) => state.auctionSlice || {});
const { filterByProvince } = useSelector((state) => state.auctionSlice || {});
const {
filterByQuantity,
filterByAge,
filterByRace,
filterByWeight,
filterByPrice,
} = useSelector((state) => state.auctionSlice || {});
const [tableRows, setTableRows] = useState([]);
const [minQuantity, setMinQuantity] = useState(0);
const [maxQuantity, setMaxQuantity] = useState(0);
// useEffect(() => {
// setInterval(() => {
// dispatch(auctionGetAuctions());
// }, 60000);
// }, []);
useEffect(() => {
dispatch(auctionGetAuctions());
}, [
filterByDate,
filterByCity,
filterByQuantity,
filterByPrice,
filterByWeight,
filterByAge,
filterByRace,
]);
useEffect(() => {
if (auctions && Array.isArray(auctions) && auctions.length > 0) {
const quantities = auctions
.map((object) => object?.quantity)
.filter((qty) => qty !== undefined && qty !== null);
if (quantities.length > 0) {
setMinQuantity(Math.min(...quantities));
setMaxQuantity(Math.max(...quantities));
}
}
}, [auctions]);
useEffect(() => {
if (auctions && Array.isArray(auctions)) {
let data = [];
const dateFilterCallback = (item) => {
if (!filterByDate || !Array.isArray(filterByDate)) {
return true;
}
if (!item?.sendDate) {
return false;
}
try {
const formattedDate = format(new Date(item.sendDate), "yyyy/MM/dd");
return filterByDate.includes(formattedDate);
} catch (error) {
console.error("Error formatting date:", error);
return false;
}
};
const provinceCityFilterCallback = (item) => {
if (filterByCity || filterByProvince) {
if (filterByCity) {
return item?.process?.city?.cityOperatorCity === filterByCity;
} else if (filterByProvince) {
return (
item?.process?.city?.cityOperatorProvince === filterByProvince
);
}
}
return true;
};
const advanceFilterCallback = (item) => {
if (filterByRace) {
return item?.chickenBreed === filterByRace;
}
if (filterByQuantity && Array.isArray(filterByQuantity)) {
const quantity = item?.quantity;
if (quantity === undefined || quantity === null) {
return false;
}
return (
quantity >= (filterByQuantity[0] || 0) &&
quantity <= (filterByQuantity[1] || Infinity)
);
}
if (filterByPrice && Array.isArray(filterByPrice)) {
if (item?.process?.killHouseAuctionsList) {
const auctionsList = item.process.killHouseAuctionsList;
if (Array.isArray(auctionsList) && auctionsList.length > 0) {
const lastAuction = auctionsList[auctionsList.length - 1];
const fee = lastAuction?.fee;
if (fee === undefined || fee === null) {
return false;
}
return (
fee >= (filterByPrice[0] || 0) &&
fee <= (filterByPrice[1] || Infinity)
);
}
}
return false;
}
if (filterByWeight && Array.isArray(filterByWeight)) {
const weight = item?.IndexWeight;
if (weight === undefined || weight === null) {
return false;
}
return (
weight >= (filterByWeight[0] || 0) &&
weight <= (filterByWeight[1] || Infinity)
);
}
if (filterByAge !== undefined && filterByAge !== null) {
const age = item?.process?.poultry?.age;
if (age === undefined || age === null) {
return false;
}
return age <= filterByAge;
}
return true;
};
if (
filterByDate ||
filterByCity ||
filterByProvince ||
filterByQuantity ||
filterByPrice ||
filterByWeight ||
filterByAge !== undefined ||
filterByRace
) {
data = auctions
.filter(dateFilterCallback)
.filter(provinceCityFilterCallback)
.filter(advanceFilterCallback)
.map((item, i) => {
return getAuctionItemForTable(item, i, dispatch);
});
} else {
data = auctions.map((item, i) => {
return getAuctionItemForTable(item, i, dispatch);
});
}
setTableRows(data);
} else {
setTableRows([]);
}
}, [
auctions,
filterByDate,
filterByCity,
filterByProvince,
filterByQuantity,
filterByPrice,
filterByWeight,
filterByAge,
filterByRace,
dispatch,
]);
const columns =
getRoleFromUrl() === "KillHouse"
? [
"شماره مزایده",
"کدسفارش",
// "فروشنده",
"قیمت کف",
"قیمت سقف",
"بالاترین قیمت پیشنهادی",
"تعداد",
"تاریخ درخواست کشتار",
"نژاد و سن به روز",
"وزن",
"استان",
"شهرستان",
"زمان تا پایان",
"عملیات",
]
: [
"شماره مزایده",
"کدسفارش",
// "فروشنده",
"قیمت کف",
"قیمت سقف",
"بالاترین قیمت پیشنهادی",
"تعداد",
"تاریخ درخواست کشتار",
"نژاد و سن به روز",
"وزن",
"استان",
"شهرستان",
"زمان تا پایان",
];
return (
<Grid container gap={SPACING.SMALL} p={SPACING.SMALL} width="100%">
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography color={(prop) => prop.palette.grey["A700"]}>
فیلتر کردن مزایده ها
</Typography>
</Grid>
<Grid>
<ButtonGroup variant="text" aria-label="outlined button group">
<Button
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <AuctionFilterRequestDate />,
title: " فیلتر تاریخ",
})
);
}}
>
فیلتر تاریخ
</Button>
<Button
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: " فیلتر بر اساس شهر و استان",
content: <AuctionFilterRequestZone />,
})
);
}}
>
فیلتر استان و شهرستان
</Button>
<Button
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "فیلتر پیشرفته",
content: (
<AuctionFilterRequestAdvance
maxQuantity={maxQuantity}
minQuantity={minQuantity}
/>
),
})
);
}}
>
فیلتر پیشرفته
</Button>
<Button
onClick={() => {
dispatch(LOADING_START());
dispatch(auctionFilterByDate(""));
dispatch(auctionFilterByCity(""));
dispatch(auctionFilterByProvince(""));
dispatch(auctionFilterByQuantity(""));
dispatch(auctionFilterByAge(""));
dispatch(auctionFilterByRace(""));
dispatch(auctionFilterByWeight(""));
dispatch(auctionFilterByPrice(""));
dispatch(auctionGetAuctions());
dispatch(LOADING_END());
}}
>
حذف همه فیلترها
</Button>
</ButtonGroup>
</Grid>
</Grid>
<AdvancedTable
name="مزایده های در حال انجام"
columns={columns}
data={tableRows}
/>
</Grid>
);
};

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const auctionGetAuctions = createAsyncThunk(
"AUCTION_GET_AUCTIONS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(`Poultry_Request/?type=auction`);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,16 @@
// {
// "key":"3175f8ee-ec0d-4865-b568-b0ab7880151c",
// "fee":"620000"
// }
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const AuctionOfferPrice = createAsyncThunk(
"AuctionOfferPrice",
async (d) => {
const { data, status } = await axios.post("kill_house_Request_auction/", d);
return { data, status };
}
);

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const auctionSlaughterRequests = createAsyncThunk(
"AUCTION_SLAUGHTER_REQUESTS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(`kill_house_Request_auction/`);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,64 @@
import { Button } from "@mui/material";
import moment from "moment";
import { Timer } from "../../../components/timer/Timer";
import GavelIcon from "@mui/icons-material/Gavel";
import { AuctionOfferPriceForm } from "../components/auction-offer-price-form/AuctionOfferPriceForm";
import { DRAWER } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export function getAuctionItemForTable(item, i, dispatch) {
const auctionFinishDate = moment(
new Date(item?.process?.auctionsList[0]?.date)
);
const currentDate = moment();
const diff = auctionFinishDate.diff(currentDate);
const auctionRemainedSeconds = moment.duration(diff).asSeconds();
const highestBidedPrice = item.process?.killHouseAuctionsList
? item?.process?.killHouseAuctionsList[
item?.process?.killHouseAuctionsList.length - 1
].fee
: "پیشنهادی وجود ندارد!";
return [
item.id,
item.orderCode,
// item?.process?.provinceRequestAuctionList ? "استان" : "مرغدار",
item?.process?.auctionsList[0]?.floorPrice,
item?.process?.auctionsList[0]?.ceilingPrice,
highestBidedPrice,
item.quantity,
item.sendDate,
item.chickenBreed + " - " + item?.process?.poultry?.age,
item.IndexWeight,
item?.process?.city.cityOperatorProvince,
item?.process?.city.cityOperatorCity,
<Timer key={"auctiontimer" + i} seconds={auctionRemainedSeconds} />,
getRoleFromUrl() === "KillHouse" && (
<Button
key={"auctionbidsumbmit" + i}
variant="outlined"
startIcon={<GavelIcon />}
onClick={() => {
dispatch(
DRAWER({
right: true,
size: 500,
title: "ثبت قیمت در مزایده",
content: (
<AuctionOfferPriceForm
data={item}
floorPrice={item?.process?.auctionsList[0]?.floorPrice}
ceilingPrice={item?.process?.auctionsList[0]?.ceilingPrice}
highestBidedPrice={highestBidedPrice}
auctionRemainedSeconds={auctionRemainedSeconds}
/>
),
})
);
}}
>
ثبت قیمت
</Button>
),
];
}

View File

@@ -0,0 +1,118 @@
import React from "react";
import { Grid, Divider } from "@mui/material";
import { Typography } from "@mui/material";
import { PrivacyTip, Security, Info } from "@mui/icons-material"; // Import icons
export const AuthPrivacyText = () => {
return (
<Grid
sx={{
height: "400px",
overflow: "auto",
p: 3,
borderRadius: 2,
"&::-webkit-scrollbar": {
width: "8px",
},
"&::-webkit-scrollbar-thumb": {
backgroundColor: "primary.main",
borderRadius: "4px",
},
"&::-webkit-scrollbar-track": {
backgroundColor: "grey.100",
},
}}
>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography
variant="h5"
color="primary"
sx={{
display: "flex",
alignItems: "center",
gap: 1,
}}
>
<PrivacyTip fontSize="medium" />
بيانيه حريم خصوصی
</Typography>
<Typography variant="body1" sx={{ mt: 2, color: "text.secondary" }}>
اطلاعات مربوط به هر شخص، حریم خصوصی وی محسوب میشود. حفاظت و حراست
از اطلاعات شخصی در سامانه رصد یار، نه تنها موجب حفظ امنیت کاربران
میشود، بلکه باعث اعتماد بیشتر و مشارکت آنها در فعالیتهای جاری
میگردد. هدف از این بیانیه، آگاه ساختن شما درباره ی نوع و نحوه ی
استفاده از اطلاعاتی است که در هنگام استفاده از سامانه رصد یار ، از
جانب شما دریافت میگردد. شرکت هوشمند سازان خود را ملزم به رعایت حریم
خصوصی همه شهروندان و کاربران سامانه دانسته و آن دسته از اطلاعات
کاربران را که فقط به منظور ارائه خدمات کفایت میکند، دریافت کرده و
از انتشار آن یا در اختیار قرار دادن آن به دیگران خودداری مینماید.
</Typography>
</Grid>
<Grid item xs={12}>
<Divider sx={{ my: 2 }} />
</Grid>
<Grid item xs={12}>
<Typography
variant="h6"
color="primary"
sx={{ display: "flex", alignItems: "center", gap: 1 }}
>
<Info fontSize="medium" />
چگونگی جمع آوری و استفاده از اطلاعات کاربران:
</Typography>
<Typography variant="body1" sx={{ mt: 2, color: "text.secondary" }}>
<strong>الف:</strong> اطلاعاتی که شما خود در اختيار این سامانه قرار
میدهيد، شامل موارد زيرهستند:
</Typography>
<Typography variant="body1" sx={{ pl: 2, color: "text.secondary" }}>
اقلام اطلاعاتی شامل شماره تلفن همراه، تاریخ تولد، کد پستی و کد ملی
کاربران را دریافت مینماییم که از این اقلام، صرفا جهت احراز هویت
کاربران استفاده خواهد شد.
</Typography>
<Typography variant="body1" sx={{ mt: 2, color: "text.secondary" }}>
<strong>ب:</strong> برخی اطلاعات ديگر که به صورت خودکار از شما
دريافت میشود شامل موارد زير میباشد:
</Typography>
<Typography variant="body1" sx={{ pl: 2, color: "text.secondary" }}>
دستگاهی که از طریق آن سامانه رصد یار را مشاهده مینمایید( تلفن
همراه، تبلت، رایانه). <br />
نام و نسخه سیستم عامل و browser کامپیوتر شما. <br />
اطلاعات صفحات بازدید شده. <br />
تعداد بازدیدهای روزانه در درگاه. <br /> هدف ما از دریافت این
اطلاعات استفاده از آنها در تحلیل عملکرد کاربران درگاه می باشد تا
بتوانیم در خدمت رسانی بهتر عمل کنیم.
</Typography>
</Grid>
<Grid item xs={12}>
<Divider sx={{ my: 2 }} />
</Grid>
<Grid item xs={12}>
<Typography
variant="h6"
color="primary"
sx={{ display: "flex", alignItems: "center", gap: 1 }}
>
<Security fontSize="medium" />
امنیت اطلاعات
</Typography>
<Typography variant="body1" sx={{ mt: 2, color: "text.secondary" }}>
متعهدیم که امنیت اطلاعات شما را تضمین نماییم و برای جلوگیری از هر
نوع دسترسی غیرمجاز و افشای اطلاعات شما از همه شیوههای لازم استفاده
میکنیم تا امنیت اطلاعاتی را که به صورت آنلاین گردآوری میکنیم، حفظ
شود. لازم به ذکر است در سامانه ما، ممکن است به سایت های دیگری لینک
شوید، وقتی که شما از طریق این لینکها از سامانه ما خارج میشوید،
توجه داشته باشید که ما بر دیگر سایت ها کنترل نداریم و سازمان تعهدی
بر حفظ حریم شخصی آنان در سایت مقصد نخواهد داشت و مراجعه کنندگان
میبایست به بیانیه حریم شخصی آن سایت ها مراجعه نمایند.
</Typography>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,519 @@
import React, { useContext, useEffect, useState } from "react";
import {
Typography,
Box,
Paper,
Divider,
IconButton,
Grid,
useTheme,
Button,
TextField,
Autocomplete,
} from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
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 LocationCityIcon from "@mui/icons-material/LocationCity";
import LockIcon from "@mui/icons-material/Lock";
import { useDispatch, useSelector } from "react-redux";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { formatJustDate } from "../../../../utils/formatTime";
import { getUserProfile } from "../../services/getUserProfile";
import { ChangeProfile } from "../change-profile/ChangeProfile";
import { motion } from "framer-motion";
import { ChangePasswordForm } from "../change-password-form/ChangePasswordForm";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { changeProfileFactorInfo } from "../../services/changeProfileInfo";
import { AppContext } from "../../../../contexts/AppContext";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../../slaughter-house/services/slaughter-get-provinces";
const fadeInVariant = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
export const BasicProfileInformation = () => {
const { userProfile } = useSelector((state) => state.userSlice);
const dispatch = useDispatch();
const theme = useTheme();
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const [openNotif] = useContext(AppContext);
const getProfile = () => {
dispatch(getUserProfile());
};
useEffect(() => {
getProfile();
}, [dispatch]);
const validationSchema = Yup.object({
nationalId: Yup.string().required("شناسه ملی الزامی است"),
registrationNumber: Yup.string().required("شماره ثبت الزامی است"),
economicalCode: Yup.string().required("کد اقتصادی الزامی است"),
address: Yup.string().required("نشانی الزامی است"),
unitName: Yup.string().required("نام واحد الزامی است"),
postalCode: Yup.string()
.matches(/^[0-9]{10}$/, "کد پستی باید 10 رقم باشد")
.required("کد پستی الزامی است"),
province: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
city: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
});
const formik = useFormik({
initialValues: {
nationalId: userProfile?.unitNationalId || "",
registrationNumber: userProfile?.unitRegistrationNumber || "",
address: userProfile?.unitAddress || "",
postalCode: userProfile?.unitPostalCode || "",
economicalCode: userProfile?.unitEconomicalNumber || "",
unitName: userProfile?.unitName || "",
province: userProfile?.unitProvince || "",
city: userProfile?.unitCity || "",
},
validationSchema,
onSubmit: (values) => {
dispatch(LOADING_START());
dispatch(
changeProfileFactorInfo({
userprofile_key: userProfile?.key,
unit_name: values.unitName,
unit_national_id: values.nationalId,
unit_registration_number: values.registrationNumber,
unit_province: values.province,
unit_city: values.city,
unit_postal_code: values.postalCode,
unit_address: values.address,
unit_economical_number: values.economicalCode,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(getUserProfile());
dispatch(LOADING_END());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
dispatch(LOADING_END());
});
},
});
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]);
useEffect(() => {
formik.validateForm();
}, [dispatch, userProfile]);
const userData = [
{
icon: <PersonIcon color="primary" />,
label: "نام و نام خانوادگی",
value: userProfile?.fullname || "نامشخص",
},
{
icon: <PhoneIcon color="secondary" />,
label: "موبایل",
value: userProfile?.mobile || "نامشخص",
},
{
icon: <BadgeIcon color="error" />,
label: "کدملی",
value: userProfile?.nationalId || "نامشخص",
},
{
icon: <BadgeIcon color="info" />,
label: "شماره شناسنامه",
value: userProfile?.nationalCode || "نامشخص",
},
{
icon: <CakeIcon color="success" />,
label: "تاریخ تولد",
value: userProfile?.birthday
? formatJustDate(userProfile?.birthday)
: "نامشخص",
},
{
icon: <LocationCityIcon color="warning" />,
label: "استان",
value: userProfile?.province || "نامشخص",
},
{
icon: <LocationCityIcon color="warning" />,
label: "شهر",
value: userProfile?.city || "نامشخص",
},
];
return (
<Box
display="flex"
flexDirection="column"
gap={1}
sx={{ width: "100%", padding: 1 }}
>
<motion.div
initial="hidden"
animate="visible"
variants={fadeInVariant}
style={{ width: "100%" }}
>
<Paper
elevation={4}
sx={{
padding: 2,
borderRadius: 3,
bgcolor: theme.palette.background.paper,
}}
>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
mb={1}
>
<Box display="flex" alignItems="center" gap={1}>
<PersonIcon color="primary" />
<Typography variant="subtitle2" fontWeight="bold" color="primary">
اطلاعات کاربری
</Typography>
</Box>
<IconButton
color="primary"
size="small"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <ChangeProfile user={userProfile} />,
title: "ویرایش اطلاعات کاربری",
})
);
}}
>
<EditIcon />
</IconButton>
</Box>
<Grid container spacing={1}>
{userData.map((item, index) => (
<Grid item xs={12} sm={4} md={3} key={index}>
<Box display="flex" alignItems="center" gap={1}>
{item.icon}
<Box>
<Typography
variant="body2"
fontWeight="500"
color="text.secondary"
>
{item.label}
</Typography>
<Typography
variant="body2"
fontWeight="600"
color="text.primary"
>
{item.value}
</Typography>
</Box>
</Box>
</Grid>
))}
</Grid>
<Divider sx={{ marginY: 2 }} />
{(userProfile?.role?.includes("ProvinceOperator") ||
userProfile?.role?.includes("KillHouse")) && (
<>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
mb={2}
>
<Box display="flex" alignItems="center" gap={1}>
<PersonIcon color="primary" />
<Typography
variant="subtitle2"
fontWeight="bold"
color="primary"
>
اطلاعات صدور فاکتور
</Typography>
</Box>
</Box>
<form onSubmit={formik.handleSubmit}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="نام واحد"
name="unitName"
value={formik.values.unitName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.unitName &&
Boolean(formik.errors.unitName)
}
helperText={
formik.touched.unitName && formik.errors.unitName
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="شناسه ملی"
name="nationalId"
value={formik.values.nationalId}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.nationalId &&
Boolean(formik.errors.nationalId)
}
helperText={
formik.touched.nationalId && formik.errors.nationalId
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="شماره ثبت"
name="registrationNumber"
value={formik.values.registrationNumber}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.registrationNumber &&
Boolean(formik.errors.registrationNumber)
}
helperText={
formik.touched.registrationNumber &&
formik.errors.registrationNumber
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="کد اقتصادی"
name="economicalCode"
value={formik.values.economicalCode}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.economicalCode &&
Boolean(formik.errors.economicalCode)
}
helperText={
formik.touched.economicalCode &&
formik.errors.economicalCode
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="کد پستی"
name="postalCode"
value={formik.values.postalCode}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.postalCode &&
Boolean(formik.errors.postalCode)
}
helperText={
formik.touched.postalCode && formik.errors.postalCode
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<TextField
size="small"
fullWidth
label="نشانی"
name="address"
value={formik.values.address}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.address && Boolean(formik.errors.address)
}
helperText={
formik.touched.address && formik.errors.address
}
/>
</Grid>
<Grid item xs={12} sm={4}>
<Autocomplete
size="small"
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={
userProfile?.unitProvince
? `استان ${userProfile?.unitProvince}`
: "استان را انتخاب کنید"
}
/>
)}
/>
</Grid>
<Grid item xs={12} sm={4}>
<Autocomplete
size="small"
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={
userProfile?.unitCity
? `شهر ${userProfile?.unitCity}`
: "شهر را انتخاب کنید"
}
/>
)}
/>
</Grid>
<Grid item container xs={12} sm={4}>
<Button
disabled={!formik.isValid}
type="submit"
variant="contained"
color="primary"
>
ثبت
</Button>
</Grid>
</Grid>
</form>
</>
)}
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
mt={2}
>
<Box display="flex" alignItems="center" gap={1}>
<LockIcon color="secondary" />
<Typography
variant="subtitle2"
fontWeight="bold"
color="secondary"
>
تغییر رمز عبور
</Typography>
</Box>
<IconButton
color="secondary"
size="small"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "تغییر رمز عبور",
content: <ChangePasswordForm />,
})
);
}}
>
<EditIcon />
</IconButton>
</Box>
</Paper>
</motion.div>
</Box>
);
};

View File

@@ -0,0 +1,296 @@
import {
Button,
FormControl,
InputAdornment,
InputLabel,
MenuItem,
Select,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import SaveIcon from "@mui/icons-material/Save";
import { PropTypes } from "prop-types";
import { changeBankForm } from "../../services/change-bank-form";
import { useDispatch } from "react-redux";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { updateBankForm } from "../../services/update-bank-form";
import { avicultureGetProfile } from "../../../aviculture/services/aviculture-get-profile";
import { slaughterGetProfile } from "../../../slaughter-house/services/slaughter-get-profile";
import { provinceGetProfile } from "../../../province/services/province-get-profile";
import { cityGetProfile } from "../../../city/services/city-get-profile";
import { slaughterHouseVetGetProfile } from "../../../slaughter-house-vet/services/slaughter-house-vet-get-profile";
import { vatFarmGetProfile } from "../../../vet-farm/services/vet-farm-get-profile";
export const ChangeBankForm = ({ item }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
bankName: item?.userBankInfo?.bankName,
cardNumber: item?.userBankInfo?.card,
accountNumber: item?.userBankInfo?.account,
shabaNumber: item?.userBankInfo?.shaba,
accountHolder: item?.userBankInfo?.nameOfBankUser,
},
validationSchema: Yup.object({
cardNumber: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا شماره کارتتان را وارد کنید!"),
accountNumber: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا شماره حسابتان را وارد کنید!"),
shabaNumber: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا شماره شبا را وارد کنید!"),
accountHolder: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا نام صاحب حساب را وارد کنید!"),
}),
});
return (
<Grid container gap={SPACING.SMALL} direction="column" display={"flex"}>
<Grid container gap={SPACING.SMALL} direction={"column"}>
<Grid>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">بانک</InputLabel>
<Select
fullWidth
value={formik.values.bankName}
id="bankName"
label="بانک"
onChange={(e) => {
formik.setFieldValue("bankName", e.target.value);
}}
>
<MenuItem value={"موسسه افضل توس"}>موسسه افضل توس</MenuItem>
<MenuItem value={"انصار"}>انصار</MenuItem>
<MenuItem value={"سپه"}>سپه</MenuItem>
<MenuItem value={"دی"}>دی</MenuItem>
<MenuItem value={"کاب"}>اقتصاد نوین</MenuItem>
<MenuItem value={"گردشگری"}>گردشگری</MenuItem>
<MenuItem value={"حکمت ایرانیان"}>حکمت ایرانیان</MenuItem>
<MenuItem value={"ایران زمین"}>ایران زمین</MenuItem>
<MenuItem value={"کشاورزی"}>کشاورزی</MenuItem>
<MenuItem value={"مسکن"}>مسکن</MenuItem>
<MenuItem value={"مهر ایران"}>مهر ایران</MenuItem>
<MenuItem value={"مهر اقتصاد"}>مهر اقتصاد</MenuItem>
<MenuItem value={"ملت"}>ملت</MenuItem>
<MenuItem value={"ملی"}>ملی</MenuItem>
<MenuItem value={"پارسیان"}>پارسیان</MenuItem>
<MenuItem value={"پاسارگاد"}>پاسارگاد</MenuItem>
<MenuItem value={"پست بانک ایران"}>پست بانک ایران</MenuItem>
<MenuItem value={"صادرات"}>صادرات</MenuItem>
<MenuItem value={"سامان"}>سامان</MenuItem>
<MenuItem value={"صنعت و معدن"}>صنعت و معدن</MenuItem>
<MenuItem value={"سرمایه"}>سرمایه</MenuItem>
<MenuItem value={"شهر"}>شهر</MenuItem>
<MenuItem value={"سینا"}>سینا</MenuItem>
<MenuItem value={"تجارت"}>تجارت</MenuItem>
<MenuItem value={"موسسه اعتباری توسعه"}>
موسسه اعتباری توسعه
</MenuItem>
<MenuItem value={"خاورمیانه"}>خاورمیانه</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid>
<TextField
fullWidth
id="cardNumber"
label="شماره کارت"
value={formik.values.cardNumber}
error={
formik.touched.cardNumber
? Boolean(formik.errors.cardNumber)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.cardNumber && Boolean(formik.errors.cardNumber)
? formik.errors.cardNumber
: null
}
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<TextField
fullWidth
id="accountNumber"
label="شماره حساب"
value={formik.values.accountNumber}
error={
formik.touched.accountNumber
? Boolean(formik.errors.accountNumber)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.accountNumber &&
Boolean(formik.errors.accountNumber)
? formik.errors.accountNumber
: null
}
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<TextField
fullWidth
id="shabaNumber"
label="شماره شبا"
value={formik.values.shabaNumber}
error={
formik.touched.shabaNumber
? Boolean(formik.errors.shabaNumber)
: null
}
InputProps={{
endAdornment: <InputAdornment position="end">IR</InputAdornment>,
}}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.shabaNumber && Boolean(formik.errors.shabaNumber)
? formik.errors.shabaNumber
: null
}
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<TextField
fullWidth
id="accountHolder"
label="نام صاحب حساب"
value={formik.values.accountHolder}
error={
formik.touched.accountHolder
? Boolean(formik.errors.accountHolder)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.accountHolder &&
Boolean(formik.errors.accountHolder)
? formik.errors.accountHolder
: null
}
variant="outlined"
/>
</Grid>
<Grid>
<Button
onClick={() => {
dispatch(LOADING_START());
if (item.userBankInfo) {
dispatch(
updateBankForm({
name_of_bank_user: formik.values.accountHolder,
bank_name: formik.values.bankName,
card: formik.values.cardNumber,
shaba: formik.values.shabaNumber,
account: formik.values.accountNumber,
key: item.userBankInfo.key,
})
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(avicultureGetProfile());
dispatch(slaughterGetProfile());
dispatch(provinceGetProfile());
dispatch(cityGetProfile());
dispatch(slaughterHouseVetGetProfile());
dispatch(vatFarmGetProfile());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
changeBankForm({
name_of_bank_user: formik.values.accountHolder,
bank_name: formik.values.bankName,
card: formik.values.cardNumber,
shaba: formik.values.shabaNumber,
account: formik.values.accountNumber,
key: item.key ? item.key : null,
})
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(avicultureGetProfile());
dispatch(slaughterGetProfile());
dispatch(provinceGetProfile());
dispatch(cityGetProfile());
dispatch(slaughterHouseVetGetProfile());
dispatch(vatFarmGetProfile());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
}}
fullWidth
size="large"
variant="contained"
startIcon={<SaveIcon />}
>
ذخیره تغییرات
</Button>
</Grid>
</Grid>
</Grid>
);
};
ChangeBankForm.propTypes = {
item: PropTypes.object,
};

View File

@@ -0,0 +1,67 @@
import { Button, Chip, Divider, Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { useDispatch } from "react-redux";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { ChangeBankForm } from "../change-bank-form/ChangeBankForm";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { SPACING } from "../../../../data/spacing";
import { PropTypes } from "prop-types";
export const ChangeCardInfo = ({ item }) => {
const dispatch = useDispatch();
return (
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid textAlign="start">
<Divider textAlign="left">
<Chip
label={
<Grid display="flex" alignItems="center">
<Typography variant="body2">اطلاعات بانکی</Typography>
<Button
variant="text"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "تغییر اطلاعات بانکی",
content: <ChangeBankForm item={item} />,
})
);
}}
>
(ویرایش)
</Button>
</Grid>
}
/>
</Divider>
</Grid>
<Grid>
<SimpleTable
// name={`اطلاعات بانکی مرغداری ${item.unitName}`}
columns={[
"نام بانک",
"نام صاحب حساب",
"شماره کارت",
"شماره حساب",
"شماره شبا",
]}
data={[
[
item?.userBankInfo?.bankName,
item?.userBankInfo?.nameOfBankUser,
item?.userBankInfo?.card,
Number(item?.userBankInfo?.account),
item?.userBankInfo?.shaba,
],
]}
/>
</Grid>
</Grid>
);
};
ChangeCardInfo.propTypes = {
item: PropTypes.object,
};

View File

@@ -0,0 +1,154 @@
import { Button, TextField } from "@mui/material";
import { useFormik } from "formik";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import SaveIcon from "@mui/icons-material/Save";
import { changePassword } from "../../services/login";
import { useDispatch, useSelector } from "react-redux";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
export const ChangePasswordForm = () => {
const [openNotif] = useContext(AppContext);
const { userProfile } = useSelector((state) => state.userSlice);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
lastPassword: "",
newPassword: "",
renewPassword: "",
},
validationSchema: Yup.object({
lastPassword: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا رمز را وارد کنید!"),
newPassword: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا رمز را وارد کنید!"),
renewPassword: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا رمز را وارد کنید!"),
}),
});
return (
<Grid
container
direction="column"
flex="1"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<TextField
fullWidth
id="lastPassword"
value={formik.values.lastPassword}
error={
formik.touched.lastPassword
? Boolean(formik.errors.lastPassword)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.lastPassword && Boolean(formik.errors.lastPassword)
? formik.errors.lastPassword
: null
}
label="رمز قبلی"
type="password"
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<TextField
fullWidth
id="newPassword"
value={formik.values.newPassword}
error={
formik.touched.newPassword
? Boolean(formik.errors.newPassword)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.newPassword && Boolean(formik.errors.newPassword)
? formik.errors.newPassword
: null
}
label="رمز جدید"
type="password"
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<TextField
fullWidth
id="renewPassword"
value={formik.values.renewPassword}
error={
formik.touched.renewPassword
? Boolean(formik.errors.renewPassword)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.renewPassword &&
Boolean(formik.errors.renewPassword)
? formik.errors.renewPassword
: null
}
label="تکرار رمز جدید"
type="password"
autoComplete="current-password"
variant="outlined"
/>
</Grid>
<Grid>
<Button
fullWidth
size="large"
variant="contained"
startIcon={<SaveIcon />}
onClick={() => {
dispatch(
changePassword({
username: userProfile.mobile,
password: formik.values.newPassword,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "رمزعبور با موفقیت تغییر یافت!",
severity: "success",
});
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
}
});
}}
>
ذخیره تغییرات
</Button>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,64 @@
import React from "react";
import { Button, Typography, Box, Paper } from "@mui/material";
import { useDispatch } from "react-redux";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { ChangePasswordForm } from "../change-password-form/ChangePasswordForm";
import { SPACING } from "../../../../data/spacing";
import { motion } from "framer-motion";
const fadeInVariant = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
export const ChangePassword = () => {
const dispatch = useDispatch();
return (
<Box
display="flex"
flexDirection="column"
gap={SPACING.SMALL}
sx={{ width: "100%" }}
>
<motion.div
initial="hidden"
animate="visible"
variants={fadeInVariant}
style={{ width: "100%" }}
>
<Paper
elevation={3}
sx={{ padding: 3, borderRadius: 3, marginBottom: 2 }}
>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
mb={2}
>
<Typography variant="h6" fontWeight="bold">
تغییر رمز عبور
</Typography>
<Button
variant="contained"
size="small"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "تغییر رمز عبور",
content: <ChangePasswordForm />,
})
);
}}
>
ویرایش
</Button>
</Box>
</Paper>
</motion.div>
</Box>
);
};

View File

@@ -0,0 +1,266 @@
import { Button, TextField } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import React, { useEffect } from "react";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { PropTypes } from "prop-types";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { changeProfileInfo } from "../../services/changeProfileInfo";
import { useDispatch } from "react-redux";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { fixBase64 } from "../../../../utils/toBase64";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { getUserProfile } from "../../services/getUserProfile";
export const ChangeProfile = ({ user }) => {
const [profileImages, setProfileImages] = React.useState([]);
const [profileImg, setProfileImg] = React.useState();
const [openNotif] = useContext(AppContext);
const factorPaymentHandler = (imageList) => {
if (imageList[0]) {
setProfileImg(fixBase64(imageList[0]?.data_url));
}
setProfileImages(imageList);
};
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
firstname: user.firstName ? user.firstName : "",
lastname: user.lastName ? user.lastName : "",
natioanlId: user.nationalId ? user.nationalId : "",
natioanlCode: user.nationalCode ? user.nationalCode : "",
birthday: user.birthday ? new Date(user.birthday) : Date(),
},
validationSchema: Yup.object({
firstname: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید.!"),
lastname: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید.!"),
natioanlId: Yup.number().typeError(
"لطفا فیلد را به صورت عددی وارد کنید!"
),
natioanlCode: Yup.number().typeError(
"لطفا فیلد را به صورت عددی وارد کنید!"
),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<TextField
id="firstname"
label="نام"
variant="outlined"
sx={{ width: "100%", height: "100%" }}
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
id="lastname"
label="نام خانوادگی"
variant="outlined"
sx={{ width: "100%", height: "100%" }}
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
id="natioanlId"
label="کد ملی"
variant="outlined"
sx={{ width: "100%", height: "100%" }}
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
id="natioanlCode"
label="شماره شناسنامه"
variant="outlined"
sx={{ width: "100%", height: "100%" }}
value={formik.values.natioanlCode}
error={
formik.touched.natioanlCode
? Boolean(formik.errors.natioanlCode)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.natioanlCode && Boolean(formik.errors.natioanlCode)
? formik.errors.natioanlCode
: null
}
/>
</Grid>
<Grid display="contents">
<DatePicker
fullWidth
label="تاریخ تولد"
id="birthday"
renderInput={(params) => <TextField {...params} />}
value={formik.values.birthday}
error={
formik.touched.birthday ? Boolean(formik.errors.birthday) : null
}
onChange={(e) => {
const selectedDate = new Date(e);
// moment(e).format("YYYY-MM-DD hh:mm:ss")
formik.setFieldValue("birthday", selectedDate);
}}
onBlur={formik.handleBlur}
helperText={
formik.touched.birthday && Boolean(formik.errors.birthday)
? formik.errors.birthday
: null
}
/>
</Grid>
<Grid>
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"عکس پروفایل"}
/>
</Grid>
</Grid>
<Grid>
<Button
fullWidth
variant="contained"
disabled={!formik.isValid}
onClick={() => {
const birthday = moment(new Date(formik.values.birthday)).format(
"YYYY-MM-DD hh:mm:ss"
);
if (
formik.values.firstname ||
formik.values.lastname ||
formik.values.natioanlCode ||
formik.values.natioanlId ||
profileImg
) {
dispatch(LOADING_START());
dispatch(
changeProfileInfo({
type: "self_profile",
fullname:
formik.values.firstname + " " + formik.values.lastname,
first_name: formik.values.firstname
? formik.values.firstname
: null,
last_name: formik.values.lastname
? formik.values.lastname
: null,
national_code: formik.values.natioanlCode
? formik.values.natioanlCode
: null,
national_id: formik.values.natioanlId
? formik.values.natioanlId
: null,
birthday: birthday,
image: profileImg ? profileImg : "",
person_type: "self",
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(getUserProfile());
dispatch(LOADING_END());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
dispatch(LOADING_END());
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا حداقل یکی از موارد را تغییر دهید.",
severity: "error",
});
}
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
);
};
ChangeProfile.propTypes = {
user: PropTypes.any,
};

View File

@@ -0,0 +1,507 @@
import {
Button,
IconButton,
InputAdornment,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import {
forgetPassword,
forgetPasswordChangePassword,
forgetPasswordSendOpt,
loginWithPassword,
} from "../../services/login";
import { PropTypes } from "prop-types";
import { Yup } from "../../../../lib/yup/yup";
import { useContext, useEffect } from "react";
import {
CLOSE_MODAL,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { useState } from "react";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterGetPermisionState } from "../../../slaughter-house/services/slaughter-get-permision";
import { getUserAnnouncement } from "../../services/get-announcement";
import { getUserMovingTextsService } from "../../services/getUserMovingTexts";
const modalContentStyle = {
backgroundColor: "#ffffff",
padding: "20px",
borderRadius: "4px",
outline: "none",
minWidth: "300px",
maxWidth: "600px",
textAlign: "center",
};
const descriptionStyle = {
marginBottom: "10px",
};
const buttonStyle = {
marginTop: "10px",
};
export const EnterPassword = ({ mobile, isUserHandler }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formikMain = useFormik({
initialValues: {
password: "",
},
validationSchema: Yup.object({
password: Yup.mixed().required("این فیلد اجباری است!"),
}),
});
const getUserMovingTexts = () => {
dispatch(getUserMovingTextsService());
};
const getSlaughterState = (role) => {
dispatch(slaughterGetPermisionState());
dispatch(getUserAnnouncement(role)).then((r) => {
if (r.payload.data?.active) {
dispatch(
OPEN_MODAL({
title: "اطلاعیه سیستم",
content: (
<Grid style={modalContentStyle}>
<Typography
variant="body1"
color="secondary"
style={descriptionStyle}
>
{r.payload.data?.description}
</Typography>
<Grid container justifyContent="center">
<Grid item xs={12}>
<Button
color="secondary"
variant="outlined"
onClick={() => dispatch(CLOSE_MODAL())}
style={buttonStyle}
>
متوجه شدم
</Button>
</Grid>
</Grid>
</Grid>
),
})
);
}
});
};
const formik = useFormik({
initialValues: {
forgetOtp: "",
newPassword: "",
newPasswordRepeat: "",
},
validationSchema: Yup.object({
forgetOtp: Yup.mixed().required("این فیلد اجباری است!"),
newPassword: Yup.mixed().required("این فیلد اجباری است!"),
newPasswordRepeat: Yup.mixed().required("این فیلد اجباری است!"),
}),
});
const [showPassword, setShowPassword] = useState(false);
const [forgetPasswordKey, setForgetPasswordKey] = useState(null);
const [isForgetOtpCodeCorrect, setIsForgetOtpCodeCorrect] = useState(false);
const handleClickShowPassword = () => {
setShowPassword(!showPassword);
};
const handleMouseDownPassword = (event) => {
event.preventDefault();
};
useEffect(() => {
formik.validateForm();
formikMain.validateForm();
}, []);
return (
<>
{forgetPasswordKey ? (
<>
{isForgetOtpCodeCorrect ? (
<Grid>
<Typography variant="body1" py={SPACING.SMALL}>
رمزعبور جدید خود را وارد کنید.
</Typography>
<Grid container gap={SPACING.SMALL}>
<TextField
type={showPassword ? "text" : "password"}
id="newPassword"
label="رمزعبور جدید"
variant="outlined"
onChange={formik.handleChange}
value={formik.values.newPassword}
onBlur={formik.handleBlur}
error={
formik.touched.newPassword
? Boolean(formik.errors.newPassword)
: null
}
helperText={
formik.touched.newPassword &&
Boolean(formik.errors.newPassword)
? formik.errors.newPassword
: null
}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="نمایش رمزعبور"
onClick={handleClickShowPassword}
edge="end"
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
<TextField
type={showPassword ? "text" : "password"}
id="newPasswordRepeat"
label="تکرار رمزعبور جدید"
variant="outlined"
onChange={formik.handleChange}
value={formik.values.newPasswordRepeat}
onBlur={formik.handleBlur}
error={
formik.touched.newPasswordRepeat
? Boolean(formik.errors.newPasswordRepeat)
: null
}
helperText={
formik.touched.newPasswordRepeat &&
Boolean(formik.errors.newPasswordRepeat)
? formik.errors.newPasswordRepeat
: null
}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="نمایش رمزعبور"
onClick={handleClickShowPassword}
edge="end"
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
</Grid>
<Grid container mt={SPACING.SMALL} gap={SPACING.MEDIUM}>
<Grid flexGrow="1">
<Button
variant="contained"
disabled={
!(
formik.values.newPassword ===
formik.values.newPasswordRepeat
)
}
fullWidth
onClick={() => {
dispatch(LOADING_START());
dispatch(
forgetPasswordChangePassword({
username: mobile,
password: formik.values.newPassword,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "رمزعبور با موفقیت تغییر یافت!",
severity: "success",
});
setForgetPasswordKey(null);
}
dispatch(LOADING_END());
});
}}
>
تغییر رمزعبور
</Button>
</Grid>
</Grid>
</Grid>
) : (
<Grid>
<Typography variant="body1" py={SPACING.SMALL}>
کد یکبارمصرف دریافتی را وارد کنید.
</Typography>
<TextField
type={showPassword ? "text" : "password"}
id="forgetOtp"
label="کد دریافتی"
variant="outlined"
onChange={formik.handleChange}
value={formik.values.forgetOtp}
onBlur={formik.handleBlur}
error={
formik.touched.forgetOtp
? Boolean(formik.errors.forgetOtp)
: null
}
helperText={
formik.touched.forgetOtp && Boolean(formik.errors.forgetOtp)
? formik.errors.forgetOtp
: null
}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="نمایش کد"
onClick={handleClickShowPassword}
edge="end"
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
<Grid container mt={SPACING.SMALL} gap={SPACING.MEDIUM}>
<Grid flexGrow="1">
<Button
variant="contained"
disabled={!formik.values.forgetOtp}
fullWidth
onClick={() => {
dispatch(LOADING_START());
dispatch(
forgetPasswordSendOpt({
key: forgetPasswordKey,
code: formik.values.forgetOtp,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
setIsForgetOtpCodeCorrect(r.payload.data.code);
}
dispatch(LOADING_END());
});
}}
>
ثبت
</Button>
</Grid>
</Grid>
</Grid>
)}
</>
) : (
<>
<Typography variant="body1" py={SPACING.SMALL}>
رمز عبور را وارد کرده و بر روی ورود کلیک نمایید.
</Typography>
<TextField
type={showPassword ? "text" : "password"}
id="password"
label="رمزعبور"
variant="outlined"
onChange={formikMain.handleChange}
value={formikMain.values.password}
onBlur={formikMain.handleBlur}
onKeyPress={(e) => {
if (e.key === "Enter") {
dispatch(LOADING_START());
dispatch(
loginWithPassword({
mobile,
password: formikMain.values.password,
})
).then((r) => {
if (
r.payload?.data?.role?.includes("CityOperator") ||
r.payload?.data?.role?.includes("KillHouse") ||
r.payload?.data?.role?.includes("CityJahad") ||
r.payload?.data?.role?.includes("ProvinceSupervisor") ||
r.payload?.data?.role?.includes("ProvinceOperator")
) {
getUserMovingTexts();
}
if (r.payload?.data?.role?.includes("KillHouse")) {
getSlaughterState("KillHouse");
} else if (r.payload?.data?.role?.includes("CityOperator")) {
getSlaughterState("CityOperator");
}
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "رمزعبور اشتباه است!",
severity: "error",
});
}
dispatch(LOADING_END());
});
}
}}
error={
formikMain.touched.password
? Boolean(formikMain.errors.password)
: null
}
helperText={
formikMain.touched.password && Boolean(formikMain.errors.password)
? formikMain.errors.password
: null
}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="نمایش رمز عبور"
onClick={handleClickShowPassword}
edge="end"
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</InputAdornment>
),
}}
/>
<Grid
container
direction="column"
mt={SPACING.SMALL}
gap={SPACING.SMALL}
>
<Grid container>
<Grid flexGrow="1">
<Button
variant="contained"
disabled={!formikMain.isValid}
fullWidth
onClick={() => {
dispatch(LOADING_START());
dispatch(
loginWithPassword({
mobile,
password: formikMain.values.password,
})
).then((r) => {
if (r.payload?.data?.role?.includes("KillHouse")) {
getSlaughterState("KillHouse");
} else if (
r.payload?.data?.role?.includes("CityOperator")
) {
getSlaughterState("CityOperator");
}
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "رمزعبور اشتباه است!",
severity: "error",
});
}
dispatch(LOADING_END());
});
}}
>
ورود به سامانه
</Button>
</Grid>
<Grid
flexGrow="1"
alignItems="center"
alignSelf="center"
justifyContent="center"
display="flex"
>
<Button
onClick={() => {
isUserHandler();
}}
>
بازگشت به صفحه قبل
</Button>
</Grid>
</Grid>
<Grid flexGrow="1">
<Button
variant="outlined"
fullWidth
onClick={() => {
dispatch(LOADING_START());
dispatch(
forgetPassword({
mobile,
state: "forget_password",
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
setForgetPasswordKey(r.payload.data.key);
}
dispatch(LOADING_END());
});
}}
>
رمزعبور را فراموش کرده ام (استفاده از پیامک)
</Button>
</Grid>
</Grid>
</>
)}
</>
);
};
EnterPassword.propTypes = {
mobile: PropTypes.any,
isUserHandler: PropTypes.any,
};

View File

@@ -0,0 +1,190 @@
import { Box, Button, Chip, Divider, Typography } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { useLocation } from "react-router-dom";
import {
Home,
Business,
House,
LocalHospital,
DirectionsCar,
AccountBalance,
Public,
} from "@mui/icons-material"; // Example icons
import { motion } from "framer-motion";
import useUserProfile from "../../hooks/useUserProfile";
import {
DRIVER_USER_PROFILE,
ROUTE_AVICULTURE_USER_PROFILE,
ROUTE_CHAIN_COMPANY_USER_PROFILE,
ROUTE_CITYVET_USER_PROFILE,
ROUTE_CITY_USER_PROFILE,
ROUTE_GENERAL_USER_PROFILE,
ROUTE_STEWARD_USER_PROFILE,
ROUTE_INSPECTOR_USER_PROFILE,
ROUTE_LIVE_STOCK_USER_PROFILE,
ROUTE_PROVINCE_FINANCIAL_USER_PROFILE,
ROUTE_PROVINCE_USER_PROFILE,
ROUTE_SLAUGHTER_HOUSE_VET_USER_PROFILE,
ROUTE_SLAUGHTER_USER_PROFILE,
ROUTE_VETFARM_USER_PROFILE,
} from "../../../../routes/routes";
import { Grid } from "../../../../components/grid/Grid";
const routesMap = {
General: {
path: ROUTE_GENERAL_USER_PROFILE,
label: "کاربری",
icon: <Home />,
},
CityOperator: {
path: ROUTE_CITY_USER_PROFILE,
label: "شهرستان",
icon: <Business />,
},
Poultry: {
path: ROUTE_AVICULTURE_USER_PROFILE,
label: "مرغداری",
icon: <House />,
},
ProvinceOperator: {
path: ROUTE_PROVINCE_USER_PROFILE,
label: "تخصیص استان",
icon: <AccountBalance />,
},
KillHouse: {
path: ROUTE_SLAUGHTER_USER_PROFILE,
label: "کشتارگاه",
icon: <LocalHospital />,
},
VetFarm: {
path: ROUTE_VETFARM_USER_PROFILE,
label: "دامپزشک فارم",
icon: <LocalHospital />,
},
KillHouseVet: {
path: ROUTE_SLAUGHTER_HOUSE_VET_USER_PROFILE,
label: "دامپزشک کشتارگاه",
icon: <LocalHospital />,
},
Driver: {
path: DRIVER_USER_PROFILE,
label: "راننده",
icon: <DirectionsCar />,
},
ProvinceFinancial: {
path: ROUTE_PROVINCE_FINANCIAL_USER_PROFILE,
label: "مالی",
icon: <AccountBalance />,
},
ProvinceInspector: {
path: ROUTE_INSPECTOR_USER_PROFILE,
label: "بازرس",
icon: <Public />,
},
Guilds: {
path: ROUTE_STEWARD_USER_PROFILE,
label: "صنف",
icon: <Business />,
},
CityVet: {
path: ROUTE_CITYVET_USER_PROFILE,
label: "دامپزشک شهرستان",
icon: <LocalHospital />,
},
LiveStockSupport: {
path: ROUTE_LIVE_STOCK_USER_PROFILE,
label: "پشتیبانی امور دام",
icon: <LocalHospital />,
},
ChainCompany: {
path: ROUTE_CHAIN_COMPANY_USER_PROFILE,
label: "شرکت زنجیره",
icon: <Business />,
},
};
export const GeneralDashboardOperations = () => {
const { pathname } = useLocation();
const [roles] = useUserProfile();
return (
<Box p={SPACING.SMALL} display="flex" flexDirection="column">
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={1}
direction={{ xs: "column", md: "row" }}
>
<Divider sx={{ width: "100%" }}>
<Chip
label={
<Typography
variant="h6"
sx={{ fontSize: "18px", color: "darkcyan" }}
>
نقش ها
</Typography>
}
/>
</Divider>
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.2 }}
>
<NavLink
to={ROUTE_GENERAL_USER_PROFILE}
active={
pathname === ROUTE_GENERAL_USER_PROFILE || pathname === "/"
? "true"
: null
}
style={{ textDecoration: "none" }}
>
<Button
variant="outlined"
color="primary"
fullWidth
startIcon={<Home />}
sx={{ borderRadius: 3, padding: "4px 8px" }}
>
حساب کاربری
</Button>
</NavLink>
</motion.div>
{roles.map((role, i) => {
const route = routesMap[role];
return route ? (
<motion.div
key={role + i}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, delay: i * 0.1 }}
>
<NavLink
to={route.path}
active={pathname === route.path ? "true" : null}
style={{ textDecoration: "none" }}
>
<Button
variant="outlined"
fullWidth
size="small"
startIcon={route.icon}
sx={{ borderRadius: 3, padding: "4px 8px", color: "gray" }}
>
{route.label}
</Button>
</NavLink>
</motion.div>
) : null;
})}
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,163 @@
import { Button, TextField, Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { loginSendMobile } from "../../services/login";
import { useDispatch } from "react-redux";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import Captcha from "../../../../components/captcha/Captcha";
import { useEffect, useState } from "react";
import { EnterPassword } from "../enter-password/EnterPassword";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
export const Login = () => {
const [openNotif] = useContext(AppContext);
const [isValidCaptcha, setIsValidCaptcha] = useState(false);
const [isUser, setIsUser] = useState(false);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
mobile: "",
captcha: "",
},
validationSchema: Yup.object({
mobile: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا اعداد انگلیسی وارد کنید!")
.test("len", "شماره تلفن باید 11 رقم باشد!", (val, context) => {
return context.originalValue && context.originalValue.length === 11;
}),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
const isUserHandler = () => {
setIsUser(false);
setIsValidCaptcha(false);
};
const submitForm = (e) => {
e.preventDefault();
dispatch(LOADING_START());
dispatch(
loginSendMobile({
mobile: formik.values.mobile,
state: "",
})
).then((r) => {
dispatch(LOADING_END());
if (!r.payload.data.isUser) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "شماره تلفن شما در سامانه وجود ندارد!",
severity: "error",
});
}
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی به وجود آمده است!",
severity: "error",
});
} else {
setIsUser(r.payload.data.isUser);
}
});
};
return (
<Grid>
<Grid container justifyContent="center" alignItems="center">
<Grid
container
mx={{ xs: SPACING.SMALL, sm: SPACING.SMALL, md: 0 }}
justifyContent="center"
>
<Grid p={SPACING.MEDIUM} minWidth={{ md: "520px" }}>
{isUser ? (
<EnterPassword
isUserHandler={isUserHandler}
mobile={formik.values.mobile}
/>
) : (
<>
<Typography variant="h6" color="primary">
سامانه رصدیار
</Typography>
<Typography variant="body1" py={SPACING.SMALL}>
شماره موبایل تان را وارد کنید.
</Typography>
<form onSubmit={submitForm}>
<Grid>
<TextField
id="mobile"
label="شماره تلفن"
variant="outlined"
sx={{
width: "100%",
}}
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
container
justifyContent="space-between"
flexWrap="nowrap"
sx={{
flexDirection: { xs: "column", sm: "row" },
}}
gap={SPACING.SMALL}
mt={SPACING.SMALL}
>
<Captcha onChange={(status) => setIsValidCaptcha(status)} />
</Grid>
<Grid
container
justifyContent="space-between"
gap={SPACING.MEDIUM}
direction="row"
mt={SPACING.SMALL}
>
<Grid flexGrow="2">
<Button
variant="contained"
fullWidth
type="submit"
disabled={!(isValidCaptcha && formik.isValid)}
onSubmit={submitForm}
>
ادامه
</Button>
</Grid>
</Grid>
</form>
</>
)}
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,26 @@
import { useSelector } from "react-redux";
/**
* Hook to get the currently selected sub user from Redux state
* @returns {Object|null} - The selected sub user object with { key, name, unit, mobile, fullname } or null
*/
export const useSelectedSubUser = () => {
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
return selectedSubUser;
};
/**
* Hook to get only the selected sub user key
* @returns {string|null} - The sub user key or null
*/
export const useSelectedSubUserKey = () => {
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
return selectedSubUser?.key || null;
};
export default useSelectedSubUser;

View File

@@ -0,0 +1,18 @@
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getUserProfile } from "../services/getUserProfile";
const useUserProfile = () => {
const dispatch = useDispatch();
const { role, userProfile } = useSelector((state) => state.userSlice);
useEffect(() => {
if (!userProfile) {
dispatch(getUserProfile());
}
}, []);
return [role, userProfile];
};
export default useUserProfile;

View File

@@ -0,0 +1,15 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { getActualRoleFromRole } from "../../../utils/getRoleFromUrl";
export const changeBankForm = createAsyncThunk(
"CHANGE_BANK_FORM",
async (d) => {
const role = window.location.pathname.split("/")[3];
const { data, status } = await axios.post(
`user-bank_card/?role=${getActualRoleFromRole(role)}`,
d
);
return { data, status };
}
);

View File

@@ -0,0 +1,21 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const changeProfileInfo = createAsyncThunk(
"CHANGE_PROFILE_INFO",
async (d) => {
const { data, status } = await axios.put("system_user_profile/0/", d);
return { data, status };
}
);
export const changeProfileFactorInfo = createAsyncThunk(
"CHANGE_PROFILE_FACTOR_INFO",
async (d) => {
const { data, status } = await axios.put(
"system_user_profile-for-factor/0/",
d
);
return { data, status };
}
);

View File

@@ -0,0 +1,23 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const getAnnouncement = createAsyncThunk(
"GET_ANNOUNCEMENT",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("announcements/?total=true");
dispatch(LOADING_END());
return { data, status };
}
);
export const getUserAnnouncement = createAsyncThunk(
"GET_ANNOUNCEMENT",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(`announcements/?role=${d}`);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getUnseenMessages = createAsyncThunk(
"GET_UNSEEN_MESSAGES",
async (d, { dispatch }) => {
const { data, status } = await axios.get(`get_num_message/`);
return { data, status };
}
);

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const getUserMovingTextsService = createAsyncThunk(
"GET_USER_MOVING_TEXTS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(`moving-text/?dashboard=true`);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,7 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getUserProfile = createAsyncThunk("GET_USER_PROFILE", async () => {
const { data, status } = await axios.get("system_user_profile/?self-profile");
return { data, status };
});

View File

@@ -0,0 +1,23 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const getUserRoleInfo = createAsyncThunk(
"GET_USER_ROLE_INFO",
async ({ userKey, role }, { dispatch }) => {
try {
dispatch(LOADING_START());
const { data, status } = await axios.get("user-role-info/", {
params: {
user_key: userKey,
role: role,
},
});
dispatch(LOADING_END());
return { data, status };
} catch (error) {
dispatch(LOADING_END());
throw error;
}
}
);

View File

@@ -0,0 +1,98 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const loginSendMobile = createAsyncThunk(
"LOGIN_SEND_MOBILE",
async ({ mobile, state }) => {
const { data, status } = await axios.post("api/send/", { mobile, state });
return { data, status };
}
);
//buildchange
export const loginWithPassword = createAsyncThunk(
"LOGIN_WITH_PASSWORD",
async ({ mobile, password }) => {
const { data, status } = await axios.post("api/login/", {
username: mobile,
password,
// bushehr
// api_key: "44d89a44-bd44-2144-a02f-2cc2cbf4e2d4",
// kermanshah
// api_key: "4b124231-62c7-41c6-8081-66191536c0b7",
// test
// api_key: "11d89a11-bd11-2111-a02f-2cc1cbf4e1d4",
// markazi arak
// api_key: "44d89a99-bd12-4152-a02f-5cc5cbf7e5d5",
// hamedan
// api_key: "33374d79-e8ee-4947-b0b4-4a6d31935814",
// lorestan
// api_key: "2b773c7e-f6de-4dd2-af43-a012a8b78d04",
// test
// api_key: "11d89a11-bd11-2111-a02f-2cc1cbf4e1d4",
// ardebil (AR)
// api_key: "14d14a14-bd14-1414-a03f-2cc1cbf5e1d1",
// tabriz (sharghi)
// api_key: "23d23a32-bd23-2311-a03f-2cc1cbf3e1d4",
});
return { data, status };
}
);
export const forgetPassword = createAsyncThunk(
"LOGIN_FORGET_PASSWORD",
async ({ mobile, state }) => {
const { data, status } = await axios.post("api/send/", { mobile, state });
return { data, status };
}
);
export const checkUserPath = createAsyncThunk(
"CHECK_USER_PATH",
async ({ mobile, state }) => {
const { data, status } = await axios.post(
"https://userbackend.rasadyar.com/api/send_otp/",
{ mobile, state }
);
return { data, status };
}
);
export const checkActiveUsers = createAsyncThunk(
"CHECK_ACTIVE_USERS",
async () => {
const { data, status } = await axios.get(
"https://userbackend.rasadyar.com/api/active-users/"
);
return { data, status };
}
);
export const forgetPasswordSendOpt = createAsyncThunk(
"LOGIN_FORGET_PASSWORD_SEND_OTP",
async ({ key, code }) => {
const { data, status } = await axios.post("api/check/", { key, code });
return { data, status };
}
);
export const forgetPasswordChangePassword = createAsyncThunk(
"LOGIN_FORGET_PASSWORD_CHANGE_PASSWORD",
async ({ username, password }) => {
const { data, status } = await axios.post("api/forget/", {
username,
password,
});
return { data, status };
}
);
export const changePassword = createAsyncThunk(
"LOGIN_CHANGE_PASSWORD",
async ({ username, password }) => {
const { data, status } = await axios.post("api/change_password/", {
username,
password,
});
return { data, status };
}
);

View File

@@ -0,0 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const tokenVerifiction = createAsyncThunk(
"GET_USER_RAVANDNO_INFO",
async (d) => {
const { data, status } = await axios.post("token-verification/", {
token: d.token,
});
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const updateBankForm = createAsyncThunk(
"UPDATE_BANK_FORM",
async (d) => {
const { data, status } = await axios.put(`user-bank_card/0/`, d);
return { data, status };
}
);

View File

@@ -0,0 +1,15 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const wagePaymentGetUserInfo = createAsyncThunk(
"WAGE_PAYMENT_GET_USER_INFO",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
`https://${d.province}backend.rasadyar.com/get-payer-info/?${d.key}`
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,225 @@
import { Button, IconButton, TextField } from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { useDispatch, useSelector } from "react-redux";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
// import { AvicultureNewRequest } from "../aviculture-new-request/AvicultureNewRequest";
// import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
import DeleteIcon from "@mui/icons-material/Delete";
import { AppContext } from "../../../../contexts/AppContext";
import { avicultureGetRequests } from "../../services/aviculture-requests";
import { archiveDeleteKillRequestService } from "../../services/aviculture-delete-kill-request";
import { formatJustDate } from "../../../../utils/formatTime";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { CityNewKillRequest } from "../../../city/components/city-new-kill-request/CityNewKillRequest";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
export const AvicultureActiveRequests = () => {
const [dataTable, setDataTable] = useState([]);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const { avicultureRequests } = useSelector((state) => state.avicultureSlice);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
useEffect(() => {
dispatch(avicultureGetRequests({ selectedDate1, selectedDate2 }));
}, [selectedDate1, selectedDate2]);
useEffect(() => {
const filteredData =
Array.isArray(avicultureRequests) &&
avicultureRequests?.filter(
(item) =>
(item.stateProcess === "accepted" ||
item.stateProcess === "pending") &&
item.finalState !== "archive"
);
let d;
if (filteredData) {
d = filteredData?.map((item, i) => {
let sellType = "";
if (item.directBuying) {
sellType = "خرید مستقیم";
} else if (item.union) {
sellType = "خرید خارج از استان";
} else {
sellType = "اتحادیه";
}
return [
i + 1,
item.orderCode,
sellType,
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.directBuyingBuyerInfo
? `${item?.directBuyingBuyerInfo?.buyerFullname} (${item?.directBuyingBuyerInfo?.buyerMobile})`
: "-",
item.directBuyingKillPlace ? item.directBuyingKillPlace : "-",
item?.createDate ? formatJustDate(item?.createDate) : "",
item?.sendDate ? formatJustDate(item?.sendDate) : "",
`${item.process?.poultry?.poultryName} (${item.process?.poultry?.poultryMobile})`,
`${item.process?.poultry?.poultryProvince}/${item.process?.poultry?.poultryCity}`,
formatJustDate(item.process?.poultryHatching?.date),
item.process?.poultry?.age,
item.IndexWeight,
item.process?.poultry?.poultryQuantity.toLocaleString(),
(
item.IndexWeight * item.process?.poultry?.poultryQuantity
).toLocaleString(),
<IconButton
key={item?.orderCode}
disabled={item?.directBuyingKillPlace}
aria-label="delete"
color="error"
className="avicultureActiveRequestsBtn"
onClick={() => {
dispatch(
archiveDeleteKillRequestService(
item?.process?.poultry?.poultryRequestId
)
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(avicultureGetRequests());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<DeleteIcon />
</IconButton>,
];
});
}
setDataTable(d);
}, [avicultureRequests]);
return (
<>
<Grid container gap={SPACING.MEDIUM} direction="column" xs={12}>
<Grid
container
gap={SPACING.SMALL}
justifyContent={{ xs: "center", lg: "space-between" }}
alignSelf="center"
alignItems="center"
xs={12}
direction={{ xs: "column", lg: "row" }}
width="100%"
>
<Grid
container
gap={SPACING.SMALL}
alignItems={"start"}
direction={"column"}
width="100%"
>
<Grid container width="100%" gap={SPACING.SMALL}>
<Button
className="avicultureNewRequestBtn"
variant={"contained"}
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت درخواست کشتار",
content: <CityNewKillRequest isAviculture />,
})
);
}}
>
ثبت درخواست کشتار
</Button>
{/* <Button
onClick={() => setIsTourOpen(!isTourOpen)}
variant="outlined"
endIcon={<HelpOutlineIcon />}
>
راهنما
</Button> */}
</Grid>
<Grid width="100%" className="avicultureActiveRequestsView">
<Grid container alignItems="center" 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>
<ResponsiveTable
title="درخواست های کشتار"
columns={[
"ردیف",
"کد سفارش",
"نوع فروش",
"نوع کشتار",
"اطلاعات خریدار",
"محل کشتار",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"مرغدار (تلفن)",
"استان/شهر",
"تاریخ جوجه ریزی",
"سن مرغ",
"میانگین وزنی",
"تعداد",
"وزن بار (کیلوگرم)",
"حذف",
]}
data={dataTable}
/>
</Grid>
</Grid>
</Grid>
</Grid>
</>
);
};

View File

@@ -0,0 +1,72 @@
import { Card, IconButton } from "@mui/material";
import { useEffect, useState } from "react";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { useNavigate } from "react-router-dom";
import useAvicultureRequests from "../../hooks/useAvicultureRequests";
import { formatJustDate } from "../../../../utils/formatTime";
export const AvicultureArchivedRequests = () => {
const navigate = useNavigate();
const [dataTable, setDataTable] = useState([]);
const avicultureRequests = useAvicultureRequests("Poultry");
const urlRole = window.location.pathname.split("/")[1];
useEffect(() => {
const fileUrl = "/" + urlRole + "/file/";
const filteredData = avicultureRequests?.filter(
(item, i) => item.finalState === "archive"
);
const d = filteredData?.map((item, i) => {
return [
i + 1,
item.orderCode,
formatJustDate(item?.createDate),
formatJustDate(item?.sendDate),
item?.process?.poultry?.poultryName,
item?.process?.poultry?.poultryMobile,
item?.process?.poultry?.poultryCity,
item?.process?.poultry?.poultryProvince,
item?.process?.poultry?.age,
item?.process?.poultry?.poultryQuantity,
<IconButton
key={item?.orderCode}
aria-label="delete"
color="primary"
onClick={() =>
navigate(fileUrl + item?.process?.poultry?.poultryRequestId)
}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, []);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"تاریخ ثبت درخواست",
"تاریخ درخواست",
"مرغدار",
"تلفن مرغدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"مشاهده",
]);
return (
<Card>
<AdvancedTable
expandable
name={"درخواست های پایان یافته"}
columns={tableDataCol}
data={dataTable}
/>
</Card>
);
};

View File

@@ -0,0 +1,121 @@
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import {
Button,
Checkbox,
FormControlLabel,
Grid,
Typography,
} from "@mui/material";
import { avicultureGetGivePermissionService } from "../../services/aviculture-get-give-permission";
import { avicultureGivePermissionService } from "../../services/aviculture-give-permission";
import { SPACING } from "../../../../data/spacing";
const styles = {
root: {
padding: 20,
textAlign: "left",
boxShadow: "0px 0px 10px rgba(0, 0, 0, 0.1)",
borderRadius: 8,
backgroundColor: "#fff",
},
heading: {
textAlign: "right",
borderBottom: "2px solid #ccc",
paddingBottom: SPACING.SMALL,
marginBottom: SPACING.SMALL,
},
checkboxContainer: {
display: "flex",
justifyContent: "flex-start",
marginBottom: SPACING.SMALL,
},
checkbox: {
marginLeft: SPACING.SMALL,
},
button: {
marginTop: SPACING.SMALL,
},
};
export const AvicultureGivePermission = () => {
const dispatch = useDispatch();
const [checkbox1, setCheckbox1] = useState(false);
const [checkbox2, setCheckbox2] = useState(false);
const handleCheckbox1Change = (event) => {
setCheckbox1(event.target.checked);
};
const handleCheckbox2Change = (event) => {
setCheckbox2(event.target.checked);
};
useEffect(() => {
dispatch(avicultureGetGivePermissionService()).then((r) => {
if (r?.payload?.data?.length) {
setCheckbox1(r.payload.data[0]?.city);
setCheckbox2(r.payload.data[0]?.province);
} else {
setCheckbox1(false);
setCheckbox2(false);
}
});
}, [dispatch]);
return (
<Grid container style={styles.root} direction="column">
<Typography variant="h6" gutterBottom style={styles.heading}>
اجازه دسترسی
</Typography>
<Grid style={styles.checkboxContainer}>
<FormControlLabel
control={
<Checkbox
checked={checkbox1}
onChange={handleCheckbox1Change}
color="primary"
/>
}
label={
<Typography style={styles.checkbox}>
دادن وکالت به شهرستان جهت ثبت درخواست کشتار
</Typography>
}
/>
</Grid>
<Grid style={styles.checkboxContainer}>
<FormControlLabel
control={
<Checkbox
checked={checkbox2}
onChange={handleCheckbox2Change}
color="primary"
/>
}
label={
<Typography style={styles.checkbox}>
دادن وکالت به استان جهت ثبت درخواست کشتار
</Typography>
}
/>
</Grid>
<Grid style={styles.button}>
<Button
variant="contained"
color="primary"
onClick={() => {
dispatch(
avicultureGivePermissionService({
city: checkbox1,
province: checkbox2,
})
);
}}
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,242 @@
// import { IconButton, Tooltip } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import moment from "moment";
import // DRAWER,
"../../../../lib/redux/slices/appSlice";
// import { AvicultureNewHatching } from "../aviculture-new-hatching/AvicultureNewHatching";
import { avicultureGetHatchings } from "../../services/aviculture-get-hatchings";
import { useState } from "react";
// import DeleteIcon from "@mui/icons-material/Delete";
// import { avicultureDeleteHatching } from "../../services/aviculture-delete-hatching";
// import { useContext } from "react";
// import { AppContext } from "../../../../contexts/AppContext";
// import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
import Tour from "reactour";
import { formatJustDate } from "../../../../utils/formatTime";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
const steps = [
{
selector: ".first-step",
content: () => <div>برای ثبت جوجه ریزی اینجا کلیک کنید!</div>,
},
{
selector: ".second",
content: () => (
<div>در این قسمت جوجه ریزی های ثبت شده توسط شما نمایش داده می شود.</div>
),
},
];
export const AvicultureHatching = () => {
const dispatch = useDispatch();
// const [openNotif] = useContext(AppContext);
const { avicultureHatchings } = useSelector((state) => state.avicultureSlice);
const [isTourOpen, setIsTourOpen] = useState(false);
const [dataTable, setDataTable] = useState([]);
useEffect(() => {
dispatch(avicultureGetHatchings());
}, []);
useEffect(() => {
const d = avicultureHatchings?.map((item, i) => {
const killedNumber = item.quantity - item.losses - item.leftOver;
return [
i + 1,
item.allowHatching === "pending" ? "فعال" : "بایگانی شده",
item.poultry.unitName,
item.hall,
item.period,
formatJustDate(item?.createDate),
formatJustDate(item?.date),
item.breed.map((item) => {
const title = `${item.breed} (${item.mainQuantity} قطعه)`;
return <p key={item?.createDate}>{title}</p>;
}),
item.age,
item.quantity,
`${item.losses} (%${((item.losses * 100) / item.quantity).toFixed(0)})`,
killedNumber +
` (%${((killedNumber * 100) / item.quantity).toFixed(0)})`,
`${item.leftOver} (%${((item.leftOver * 100) / item.quantity).toFixed(
0
)})`,
// item.allowHatching === "pending" ? (
// <Tooltip key={i} title={"حذف جوجه ریزی"} placement="left-start">
// <IconButton
// aria-label="delete"
// color="error"
// onClick={() => {
// dispatch(LOADING_START());
// dispatch(avicultureDeleteHatching(item.key)).then((r) => {
// dispatch(LOADING_END());
// if (r.error) {
// if (r.error.message.includes("403")) {
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: "برای این جوجه ریزی درخواست کشتار ثبت شده است!",
// severity: "error",
// });
// } else if (r.error.message.includes("400")) {
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: "برای این جوجه ریزی بازرسی ثبت شده است!",
// severity: "error",
// });
// } else {
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: "مشکلی پیش آمده است!",
// severity: "error",
// });
// }
// } else {
// dispatch(avicultureGetHatchings());
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: "عملیات با موفقیت انجام شد.",
// severity: "success",
// });
// }
// });
// }}
// >
// <DeleteIcon />
// </IconButton>
// </Tooltip>
// ) : (
// "-"
// ),
];
});
setDataTable(d);
}, [avicultureHatchings]);
const formik = useFormik({
initialValues: {
noChicken: "",
slaughterDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
race: "آرین",
weight: "",
},
validationSchema: Yup.object({
noChicken: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
weight: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container gap={SPACING.MEDIUM} direction="column" xs={12}>
<Grid
container
gap={SPACING.SMALL}
justifyContent={{ xs: "center", lg: "space-between" }}
alignSelf="center"
alignItems="center"
xs={12}
direction={{ xs: "column", lg: "row" }}
>
<Grid
container
xs={12}
alignItems={"start"}
gap={SPACING.SMALL}
direction={"column"}
>
<Grid
width="100%"
container
alignItems="center"
justifyContent="space-between"
>
{/* <Grid>
<Button
className="first-step"
variant={"contained"}
disabled={true}
onClick={() => {
dispatch(
DRAWER({
title: "ثبت اطلاعات جوجه ریزی",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <AvicultureNewHatching />,
})
);
}}
>
ثبت جوجه ریزی جدید
</Button>
</Grid> */}
<Grid>
{/* <Button
onClick={() => setIsTourOpen(!isTourOpen)}
variant="outlined"
endIcon={<HelpOutlineIcon />}
>
راهنما
</Button> */}
</Grid>
</Grid>
<Tour
steps={steps}
isOpen={isTourOpen}
onRequestClose={() => setIsTourOpen(false)}
styles={{
popover: (base) => ({
...base,
borderRadius: "10px",
"--reactor-accent": "red",
}),
}}
/>
<Grid width="100%" className="second">
<ResponsiveTable
paginated
title="دوره های جوجه ریزی"
data={dataTable}
columns={[
"ردیف",
"وضعیت",
"نام فارم",
"سالن",
"دوره جوجه ریزی",
"تاریخ ثبت جوجه ریزی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"تلفات دوره",
"کشتار شده",
"مانده برای کشتار",
// "اقدام",
]}
customColors={[{ name: "وضعیت", color: "green" }]}
/>
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,254 @@
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import {
Autocomplete,
Button,
FormControl,
FormHelperText,
InputLabel,
MenuItem,
Select,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { useEffect } from "react";
import moment from "moment/moment";
import { DatePicker } from "@mui/x-date-pickers";
import { useDispatch } from "react-redux";
import { avicultureGetPoultry } from "../../services/aviculture-get-poultry";
import { useState } from "react";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { avicultureNewHatching } from "../../services/aviculture-new-hatching";
import { avicultureGetHatchings } from "../../services/aviculture-get-hatchings";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
export const AvicultureNewHatching = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [poultryKey, setPolutryKey] = useState("");
const [polutryData, setpolutryData] = useState("");
const [enableHall, setEnableHall] = useState(true);
const [numberOfhalls, setNumberOfHalls] = useState(1);
const [numberOfhallSelected, setNumberOfHallSelected] = useState(null);
const formik = useFormik({
initialValues: {
quantity: "",
hatchingDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
race: "آرین",
},
validationSchema: Yup.object({
quantity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
dispatch(LOADING_START());
dispatch(avicultureGetPoultry()).then((r) => {
setpolutryData(r.payload.data);
dispatch(LOADING_END());
});
}, []);
useEffect(() => {
if (poultryKey) {
if (numberOfhalls === 0) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "ابتدا برای این مرغداری جوجه ریزی ثبت کنید.",
severity: "error",
});
dispatch(DRAWER({ right: false, bottom: false, content: null }));
} else {
setEnableHall(false);
}
}
}, [poultryKey]);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<Autocomplete
disablePortal
id="hatching"
options={
polutryData
? polutryData.map((i) => ({
id: i.key,
label: i.unitName,
halls: i.numberOfHalls,
}))
: []
}
onChange={(event, value) => {
setPolutryKey(value.id);
setNumberOfHalls(value.halls);
}}
renderInput={(params) => (
<TextField {...params} label="محل پرورش" />
)}
/>
</Grid>
<Grid>
<Autocomplete
disabled={enableHall}
disablePortal
options={[...Array(numberOfhalls)].map((e, i) => ({
label: "سالن شماره " + (i + 1),
id: i,
}))}
onChange={(event, value) => {
setNumberOfHallSelected(value.id + 1);
}}
renderInput={(params) => (
<TextField {...params} label="سالن را انتخاب کنید" />
)}
/>
</Grid>
<NumberInput
allowLeadingZeros
thousandSeparator=","
id="quantity"
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
}
label="تعداد"
variant="outlined"
/>
<DatePicker
label="تاریخ جوجه ریزی"
id="hatchingDate"
renderInput={(params) => <TextField {...params} />}
value={formik.values.hatchingDate}
error={
formik.touched.hatchingDate
? Boolean(formik.errors.hatchingDate)
: null
}
onChange={(e) => {
formik.setFieldValue(
"hatchingDate",
moment(e).format("YYYY-MM-DD hh:mm:ss")
);
}}
onBlur={formik.handleBlur}
helperText={
formik.touched.hatchingDate && Boolean(formik.errors.hatchingDate)
? formik.errors.hatchingDate
: null
}
/>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">نژاد مرغ</InputLabel>
<Select
labelId="demo-simple-select-label"
id="race"
label="نژاد مرغ"
value={formik.values.race}
error={formik.touched.race ? Boolean(formik.errors.race) : null}
onChange={(e) => {
formik.setFieldValue("race", e.target.value);
}}
onBlur={formik.handleBlur}
>
<MenuItem value={"آرین"}>آرین</MenuItem>
<MenuItem value={"راس"}>راس</MenuItem>
<MenuItem value={"آربراکرز (آپلاس)"}>آربراکرز (آپلاس)</MenuItem>
<MenuItem value={"کاب"}>کاب</MenuItem>
<MenuItem value={"هوبارد"}>هوبارد</MenuItem>
<MenuItem value={"ترکیبی"}>ترکیبی</MenuItem>
<MenuItem value={"وارداتی"}>وارداتی</MenuItem>
</Select>
<FormHelperText>
{formik.touched.race && Boolean(formik.errors.race)
? formik.errors.race
: null}
</FormHelperText>
</FormControl>
</Grid>
<Grid>
<Button
disabled={
formik.isValid && poultryKey.length && numberOfhallSelected
? false
: true
}
fullWidth
variant="contained"
onClick={() => {
dispatch(LOADING_START());
dispatch(
avicultureNewHatching({
key: poultryKey,
quantity: formik.values.quantity,
date: formik.values.hatchingDate,
chicken_breed: formik.values.race,
hall: numberOfhallSelected,
})
).then((r) => {
if (r.error) {
if (r.error.message.includes("403")) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "قبلا برای این تاریخ یا این سالن جوجه ریزی ثبت شده است.",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
}
} else {
dispatch(avicultureGetHatchings());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
dispatch(LOADING_END());
});
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
import { Box } from "@mui/system";
import { format } from "date-fns-jalali";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { SPACING } from "../../../../data/spacing";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { ChangeCardInfo } from "../../../authentication/components/change-card-info/ChangeCardInfo";
import { avicultureGetProfile } from "../../services/aviculture-get-profile";
export const AvicultureProfile = () => {
const { profile } = useSelector((state) => state.avicultureSlice);
const dispatch = useDispatch();
useEffect(() => {
dispatch(LOADING_START());
dispatch(avicultureGetProfile()).then((r) => {
dispatch(LOADING_END());
});
}, []);
const avicultures = profile?.aviculture;
return (
<Box>
<Grid container gap={SPACING.LARGE}>
<Grid container direction="column" xs={12}>
<Grid
container
direction="column"
justifyContent="space-between"
gap={SPACING.SMALL}
>
{avicultures?.map((item, i) => (
<>
<Grid>
<SimpleTable
key={"avicultures" + i}
name={`اطلاعات مرغداری ${item.unitName}`}
columns={[
"شناسه پرورش دهنده",
"آدرس",
"تعداد سالن ها",
"تعداد پارتی",
"تاریخ آخرین پارتی",
"کد سیستمی",
"کد اپیدمیولوژیک",
]}
data={[
[
item.breedingUniqueId
? item.breedingUniqueId
: "نامشخص",
`${item.address.province.name} - ${
item.address.city.name
} ${
item.address.address
? " - " + item.address.address
: ""
}`,
item.numberOfHalls,
item.numberOfParty,
format(new Date(item?.lastPartyDate), "yyyy/MM/dd"),
item.systemCode,
item.epidemiologicalCode,
],
]}
/>
</Grid>
<Grid>
<ChangeCardInfo item={item} />
</Grid>
</>
))}
</Grid>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,79 @@
import { Card, IconButton } from "@mui/material";
import { useEffect, useState } from "react";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { useNavigate } from "react-router-dom";
import { ROUTE_AVICULTURE_FILE } from "../../../../routes/routes";
import { useDispatch, useSelector } from "react-redux";
import { avicultureGetRequests } from "../../services/aviculture-requests";
import { formatJustDate } from "../../../../utils/formatTime";
export const AvicultureRejectedRequests = () => {
const navigate = useNavigate();
const [dataTable, setDataTable] = useState([]);
const { avicultureRequests } = useSelector((state) => state.avicultureSlice);
const dispatch = useDispatch();
useEffect(() => {
dispatch(avicultureGetRequests());
}, []);
useEffect(() => {
const filteredData = avicultureRequests?.filter(
(item) =>
item.stateProcess === "rejected" || item.provinceState === "rejected"
);
const d = filteredData?.map((item, i) => {
return [
i + 1,
item.orderCode,
formatJustDate(item?.createDate),
formatJustDate(item?.sendDate),
item?.process?.poultry?.poultryName,
item?.process?.poultry?.poultryMobile,
item?.process?.poultry?.poultryCity,
item?.process?.poultry?.poultryProvince,
item?.process?.poultry?.age,
item?.process?.poultry?.poultryQuantity,
<IconButton
key={item?.orderCode}
aria-label="delete"
color="primary"
onClick={() =>
navigate(
ROUTE_AVICULTURE_FILE + item?.process?.poultry?.poultryRequestId
)
}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [avicultureRequests]);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"تاریخ ثبت درخواست",
"تاریخ درخواست",
"مرغدار",
"تلفن مرغدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"مشاهده",
]);
return (
<Card>
<AdvancedTable
name={"درخواست های رد شده"}
columns={tableDataCol}
data={dataTable}
/>
</Card>
);
};

View File

@@ -0,0 +1,84 @@
import { Card, CardActionArea, CardContent, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import ChartBar from "../../../../components/chart-bar/ChartBar";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { PropTypes } from "prop-types";
import { formatJustDate } from "../../../../utils/formatTime";
import ChartLinear from "../../../../components/chart-linear/ChartLenear";
export const AvicultureReportsCharts = ({ data }) => {
const [hatching, setHatching] = useState({ datasets: [] });
const [hatchingWeight, setHatchingWeight] = useState({ datasets: [] });
useEffect(() => {
setHatching({
labels: data?.hatchingChart?.map((data) => formatJustDate(data?.date)),
datasets: [
{
label: "تعداد",
backgroundColor: ["rgba(33, 72, 214, 0.7)"],
data: data?.hatchingChart?.map((data) => data?.quantity),
borderRadius: 5,
},
{
label: "تلفات",
backgroundColor: ["rgba(100, 130, 160, 0.7)"],
data: data.hatchingChart?.map((data) => data?.losses),
borderRadius: 5,
},
],
});
setHatchingWeight({
labels: data?.weightChart?.map((data) => formatJustDate(data?.date)),
datasets: [
{
label: "قیمت",
backgroundColor: ["rgba(33, 72, 214, 0.7)"],
data: data?.weightChart?.map((data) => data?.weight),
borderRadius: 5,
},
],
});
}, []);
return (
<Grid mb={SPACING.LARGE}>
<Grid
container
mt={SPACING.MEDIUM}
gap={SPACING.SMALL}
justifyContent="space-between"
>
<Card sx={{ width: "45%" }}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="body1">
نمودار حجم جوجه ریزی و تلفات دوره
</Typography>
<ChartBar chartData={hatching} />
</CardContent>
</CardActionArea>
</Card>
<Card sx={{ width: "45%" }}>
<CardActionArea>
<CardContent>
<Typography gutterBottom variant="body1">
نمودار پایش وزن و بهره وری وزن
</Typography>
<ChartLinear chartData={hatchingWeight} />
</CardContent>
</CardActionArea>
</Card>
</Grid>
</Grid>
);
};
AvicultureReportsCharts.propTypes = {
data: PropTypes.any,
};

View File

@@ -0,0 +1,68 @@
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 { Button } from "@mui/material";
import {
ROUTE_USER_CHANGE_BANK_ACCOUNT,
ROUTE_USER_CHANGE_PASSWORD,
ROUTE_USER_CHANGE_PROFILE_INFO,
ROUTE_USER_CHANGE_PROFILE_PICTURE,
ROUTE_USER_PROFILE,
} from "../../../../routes/routes";
export const DashboardOperations = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
>
<NavLink
to={ROUTE_USER_PROFILE}
active={
pathname === ROUTE_USER_PROFILE || pathname === "/" ? "true" : null
}
>
<Button variant="text" color="inherit">
پروفایل کاربری
</Button>
</NavLink>
<NavLink
to={ROUTE_USER_CHANGE_PASSWORD}
active={pathname === ROUTE_USER_CHANGE_PASSWORD ? "true" : null}
>
<Button variant="text" color="inherit">
تغییر رمز عبور
</Button>
</NavLink>
<NavLink
to={ROUTE_USER_CHANGE_PROFILE_PICTURE}
active={pathname === ROUTE_USER_CHANGE_PROFILE_PICTURE ? "true" : null}
>
<Button variant="text" color="inherit">
تغییر عکس پروفایل
</Button>
</NavLink>
<NavLink
to={ROUTE_USER_CHANGE_PROFILE_INFO}
active={pathname === ROUTE_USER_CHANGE_PROFILE_INFO ? "true" : null}
>
<Button variant="text" color="inherit">
تغییر مشخصات پروفایل
</Button>
</NavLink>
<NavLink
to={ROUTE_USER_CHANGE_BANK_ACCOUNT}
active={pathname === ROUTE_USER_CHANGE_BANK_ACCOUNT ? "true" : null}
>
<Button variant="text" color="inherit">
تغییر اطلاعات حساب بانکی
</Button>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,164 @@
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_AVICULTURE_ARCHIVED_REQUESTS,
ROUTE_AVICULTURE_AWAITING_INSPECTION_REQUESTS,
ROUTE_AVICULTURE_AWAITING_PAYMENT_REQUESTS,
ROUTE_AVICULTURE_GIVE_PERMISSION,
ROUTE_AVICULTURE_HATCHING,
ROUTE_AVICULTURE_REJECTED_REQUESTS,
ROUTE_AVICULTURE_SUBMIT_REQUEST,
} from "../../../../routes/routes";
import LinkItem from "../../../../components/link-item/LinkItem";
import {
FaArchive,
FaEgg,
FaMoneyBillWave,
FaRegFileAlt,
} from "react-icons/fa";
import { GrInspect } from "react-icons/gr";
import { RiFolderWarningLine } from "react-icons/ri";
export const RequestsOperations = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
style={{ placeContent: "baseline" }}
>
<Grid container direction="column" style={{ width: "100%" }}>
<Grid container gap={SPACING.SMALL} justifyContent="center">
<NavLink
to={ROUTE_AVICULTURE_HATCHING}
active={pathname === ROUTE_AVICULTURE_HATCHING ? "true" : null}
>
<LinkItem
icon={<FaEgg size={30} color="#244CCC" />}
title="جوجه ریزی"
description="ثبت و مدیریت جوجه ریزی ها"
/>
</NavLink>
{/* <NavLink
to={ROUTE_AVICULTURE_CREATE_NEW_REQUEST}
active={
pathname === ROUTE_AVICULTURE_CREATE_NEW_REQUEST ? "true" : null
}
>
<Button variant="text" color="inherit">
ثبت درخواست جدید
</Button>
</NavLink> */}
<NavLink
to={ROUTE_AVICULTURE_SUBMIT_REQUEST}
active={
pathname === ROUTE_AVICULTURE_SUBMIT_REQUEST ? "true" : null
}
>
<LinkItem
icon={<FaRegFileAlt size={30} color="#244CCC" />}
title="درخواست های کشتار"
description="مدیریت و ثبت درخواست های کشتار"
/>
</NavLink>
<NavLink
to={ROUTE_AVICULTURE_AWAITING_PAYMENT_REQUESTS}
active={
pathname === ROUTE_AVICULTURE_AWAITING_PAYMENT_REQUESTS
? "true"
: null
}
>
<LinkItem
icon={<FaMoneyBillWave size={30} color="#244CCC" />}
title="در انتظار پرداخت"
description="مشاهده درخواست های در انتظار پرداخت کشتارگاه"
/>
</NavLink>
<NavLink
to={ROUTE_AVICULTURE_AWAITING_INSPECTION_REQUESTS}
active={
pathname === ROUTE_AVICULTURE_AWAITING_INSPECTION_REQUESTS
? "true"
: null
}
>
<LinkItem
icon={
<GrInspect
className="svg-icon-color"
fill="#244CCC"
stroke="#244CCC"
style={{ color: "#4285F4" }}
color="#244CCC"
size={30}
/>
}
title="در انتظار بازرسی"
description="درخواست های در انتظار بررسی بازرس"
/>
</NavLink>
<NavLink
to={ROUTE_AVICULTURE_REJECTED_REQUESTS}
active={
pathname === ROUTE_AVICULTURE_REJECTED_REQUESTS ? "true" : null
}
>
<LinkItem
icon={
<RiFolderWarningLine
className="svg-icon-color"
color="#244CCC"
size={30}
/>
}
title="درخواست های رد شده"
description="مشاهده درخواست هایی که به دلایل مختلف توسط اتحادیه رد شده است"
/>
</NavLink>
<NavLink
to={ROUTE_AVICULTURE_ARCHIVED_REQUESTS}
active={
pathname === ROUTE_AVICULTURE_ARCHIVED_REQUESTS ? "true" : null
}
>
<LinkItem
icon={
<FaArchive
className="svg-icon-color"
color="#244CCC"
size={30}
/>
}
title="بایگانی"
description="درخواست های پایان یافته"
/>
</NavLink>
<NavLink
to={ROUTE_AVICULTURE_GIVE_PERMISSION}
active={
pathname === ROUTE_AVICULTURE_GIVE_PERMISSION ? "true" : null
}
>
<LinkItem
icon={
<FaArchive
className="svg-icon-color"
color="#244CCC"
size={30}
/>
}
title="وکالت"
/>
</NavLink>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,135 @@
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { PropTypes } from "prop-types";
import { useContext } from "react";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import { avicultureGetProfile } from "../../services/aviculture-get-profile";
import { avicultureSetHallNumber } from "../../services/aviculture-set-hall-number";
export const StrictMissingHallNumber = ({
name,
id,
avicultureKey,
handleClose,
}) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
number_of_halls: 1,
},
validationSchema: Yup.object({
number_of_halls: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container gap={SPACING.SMALL}>
<Grid>
<Typography variant="body2">
تعداد سالن های {name} با شناسه جوجه ریزی {id} را وارد کنید.
</Typography>
</Grid>
<Grid
container
justifyContent="center"
alignItems="center"
xs={12}
mt={SPACING.SMALL}
>
<FormControl sx={{ minWidth: "100%" }}>
<InputLabel id="demo-simple-select-label">تعداد سالن</InputLabel>
<Select
labelId="demo-simple-select-label"
id="type_car"
label="تعداد سالن"
value={formik.values.number_of_halls}
onChange={(e) => {
formik.setFieldValue("number_of_halls", e.target.value);
}}
error={
formik.touched.number_of_halls
? Boolean(formik.errors.number_of_halls)
: null
}
onBlur={formik.handleBlur}
helperText={
formik.touched.number_of_halls &&
Boolean(formik.errors.number_of_halls)
? formik.errors.number_of_halls
: null
}
>
<MenuItem value={"1"}>1</MenuItem>
<MenuItem value={"2"}>2</MenuItem>
<MenuItem value={"3"}>3</MenuItem>
<MenuItem value={"4"}>4</MenuItem>
<MenuItem value={"5"}>5</MenuItem>
<MenuItem value={"6"}>6</MenuItem>
<MenuItem value={"7"}>7</MenuItem>
<MenuItem value={"8"}>8</MenuItem>
<MenuItem value={"9"}>9</MenuItem>
<MenuItem value={"10"}>10</MenuItem>
</Select>
</FormControl>
</Grid>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
avicultureSetHallNumber({
key: avicultureKey,
number_of_halls: formik.values.number_of_halls,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(avicultureGetProfile());
handleClose();
}
});
}}
>
ثبت
</Button>
</Grid>
);
};
StrictMissingHallNumber.propTypes = {
name: PropTypes.any,
id: PropTypes.any,
avicultureKey: PropTypes.any,
handleClose: PropTypes.any,
};

View File

@@ -0,0 +1,49 @@
import { Typography } from "@mui/material";
import { Box } from "@mui/system";
import { useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
export const ViewBankAccount = () => {
const { userProfile } = useSelector((state) => state.userSlice);
const userData = [
["نام بانک", userProfile.userBankInfo?.bankName],
["شماره کارت", userProfile.userBankInfo?.card],
["شماره حساب", userProfile.userBankInfo?.account],
["شماره شبا", userProfile.userBankInfo?.shaba],
];
return (
<Box>
<Grid container p={SPACING.MEDIUM} direction="column">
<Grid>
<Typography variant="body1" fontWeight={"bold"}>
اطلاعات حساب بانکی
</Typography>
</Grid>
<Grid
container
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
>
{userData.map((item, i) => (
<Grid
container
gap={SPACING.SMALL}
key={i}
flexGrow="1"
flexBasis="250px"
>
<Typography variant="body2" fontWeight={"bold"}>
{item[0] + ":"}
</Typography>
<Typography variant="body2">
{item[1] ? item[1] : "نامشخص"}
</Typography>
</Grid>
))}
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,64 @@
import { Chip, Divider, Typography } from "@mui/material";
import { Box } from "@mui/system";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { formatTime } from "../../../../utils/formatTime";
import { avicultureGetProfile } from "../../services/aviculture-get-profile";
export const ViewProfile = () => {
const dispatch = useDispatch();
const { profile } = useSelector((state) => state.avicultureSlice);
useEffect(() => {
dispatch(avicultureGetProfile());
}, []);
const userProfile = profile?.profile;
const userData = [
["نام", userProfile?.firstName],
["نام خانوادگی", userProfile?.lastName],
["موبایل", userProfile?.mobile],
["استان", userProfile?.province?.name],
["شهر", userProfile?.city.name],
[
"تاریخ تولد",
userProfile?.birthday ? formatTime(userProfile?.birthday) : null,
],
];
return (
<Box>
<Grid container direction="column">
<Grid>
<Divider textAlign="left">
<Chip label="اطلاعات کاربر مرغدار" />
</Divider>
</Grid>
<Grid
container
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
>
{userData?.map((item, i) => (
<Grid
container
gap={SPACING.SMALL}
key={i}
flexGrow="1"
flexBasis="250px"
>
<Typography variant="body2" fontWeight={"bold"}>
{item[0] + ":"}
</Typography>
<Typography variant="body2">
{item[1] ? item[1] : "نامشخص"}
</Typography>
</Grid>
))}
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,26 @@
import { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { avicultureGetRequests } from "../services/aviculture-requests";
const useAvicultureRequests = () => {
const dispatch = useDispatch();
const { avicultureRequests } = useSelector((state) => state.avicultureSlice);
const [data, setData] = useState(avicultureRequests);
useEffect(() => {
dispatch(LOADING_START());
dispatch(avicultureGetRequests()).then(() => {
dispatch(LOADING_END());
});
}, []);
useEffect(() => {
setData(avicultureRequests);
}, [avicultureRequests]);
return data ? data : [];
};
export default useAvicultureRequests;

View File

@@ -0,0 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureDeleteHatching = createAsyncThunk(
"AVICULTURE_DELETE_HATCHING",
async (key) => {
const { data, status } = await axios.delete(
"poultry_hatching/0/?key=" + key
);
return { data, status };
}
);

View File

@@ -0,0 +1,18 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const archiveDeleteKillRequestService = createAsyncThunk(
"ARCHIVE_DELETE_KILL_REQUEST_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete("Poultry_Request/" + d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetChickenPrice = createAsyncThunk(
"AVICULTURE_GET_CHICKEN_PRICE",
async () => {
const { data, status } = await axios.get("pricing/?role=Poultry");
return { data, status };
}
);

View File

@@ -0,0 +1,15 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const avicultureGetDirectBuys = createAsyncThunk(
"AVICULTURE_GET_DIRECT_BUYS",
async (key, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
"kill_request/?poultry_key=" + key
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const avicultureGetGivePermissionService = createAsyncThunk(
"AVICULTURE_GET_GIVE_PERMISSION_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("poultry_allow_city_province/");
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetHallInspects = createAsyncThunk(
"AVICULTURE_GET_HALL)INSPECTS",
async (key) => {
const { data, status } = await axios.get(
"vet_farm_inspection/?poultry_key=" + key
);
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetHallsInfo = createAsyncThunk(
"AVICULTURE_GET_HALLS_INFO",
async () => {
const { data, status } = await axios.get("Poultry/?info");
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetHalls = createAsyncThunk(
"AVICULTURE_GET_HALLS",
async () => {
const { data, status } = await axios.get("poultry_hatching/");
return { data, status };
}
);

View File

@@ -0,0 +1,24 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetHatchingData = createAsyncThunk(
"VET_GET_HATCHING",
async (d) => {
const { data, status } = await axios.get("poultry_hatching/", {
params: {
key: d.key || "",
},
});
return { data, status };
}
);
export const avicultureGetHatchingDataForIncreaseHatching = createAsyncThunk(
"VET_GET_HATCHING_FOR_INCREASE",
async (key) => {
const { data, status } = await axios.get(
"poultry_hatching/?increase=true&key=" + key
);
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetHatchings = createAsyncThunk(
"AVICULTURE_GET_HATCHINGS",
async () => {
const { data, status } = await axios.get("poultry_hatching/");
return { data, status };
}
);

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const avicultureGetPoultry = createAsyncThunk(
"AVICULTURE_GET_POULTRY",
async () => {
const { data, status } = await axios.get("Poultry/", {
params: { role: getRoleFromUrl() },
});
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetProfile = createAsyncThunk(
"AVICULTURE_GET_PROFILE",
async (d, { dispatch }) => {
const { data, status } = await axios.get("Poultry/0/?profile", d);
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetReports = createAsyncThunk(
"AVICULTURE_GET_REPORTS",
async () => {
const { data, status } = await axios.get("poultry_report/");
return { data, status };
}
);

View File

@@ -0,0 +1,12 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetSlaughters = createAsyncThunk(
"AVICULTURE_GET_SLAUGHTERS",
async (d) => {
const { data, status } = await axios.get("kill_house_list/?show_poultry", {
params: { date: d.date },
});
return { data, status };
}
);

View File

@@ -0,0 +1,14 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END } from "../../../lib/redux/slices/appSlice";
export const avicultureGetUnions = createAsyncThunk(
"AVICULTURE_GET_UNIONS",
async (d, { dispatch }) => {
const { data, status } = await axios.get("show_city_operator", {
params: d,
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureGetWageType = createAsyncThunk(
"AVICULTURE_GET_WAGE_TYPE",
async () => {
const { data, status } = await axios.get("poultry-out-province-wage-type");
return { data, status };
}
);

View File

@@ -0,0 +1,16 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const avicultureGivePermissionService = createAsyncThunk(
"AVICULTURE_GIVE_PERMISSION_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.post(
"poultry_allow_city_province/",
d
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,25 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import moment from "moment/moment";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const avicultureHatchingRequestsService = createAsyncThunk(
"AVICULTURE_HATCHING_REQUESTS_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const today = "&today";
d = d || moment(new Date()).format("YYYY-MM-DD");
const { data, status } = await axios.get(
"Poultry_Request/?role=" + getRoleFromUrl() + today,
{
params: {
date1: d.selectedDate1,
date2: d.selectedDate2,
},
}
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureNewHatching = createAsyncThunk(
"VET_FARM_NEW_FARM",
async (d) => {
const { data, status } = await axios.post("poultry_hatching/", d);
return { data, status };
}
);

View File

@@ -0,0 +1,14 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const avicultureNewRequest = createAsyncThunk(
"AVICULTURE_NEW_REQUEST",
async (d) => {
try {
const { data, status } = await axios.post("Poultry_Request/", d);
return { data, status };
} catch (e) {
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,23 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const avicultureRequestsStateProcessService = createAsyncThunk(
"AVICULTURE_REQUESTS_STATE_PROCESS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
"Poultry_Request/?role=" + getRoleFromUrl(),
{
params: {
date1: d.selectedDate1,
date2: d.selectedDate2,
state_process: true,
},
}
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,19 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const avicultureGetRequests = createAsyncThunk(
"AVICULTURE_REQUESTS_SERVICE",
async (d, { dispatch }) => {
const { data, status } = await axios.get(
"Poultry_Request/?role=" + getRoleFromUrl(),
{
params: {
date1: d.selectedDate1,
date2: d.selectedDate2,
},
}
);
return { data, status };
}
);

View File

@@ -0,0 +1,13 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const avicultureSetHallNumber = createAsyncThunk(
"AVICULTURE_SET_HALL_NUMBER",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.put("Poultry/0/", d);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const provinceGetPoultry = createAsyncThunk(
"PROVINCE_GET_POULTRY",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("get-all-poultry/", {
params: {
role: getRoleFromUrl(),
active_hatching: true,
...d,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,714 @@
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import React, { useContext, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { formatTime } from "../../../../utils/formatTime";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { PageTable } from "../../../../components/page-table/PageTable";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
export const ChainCompanyActiveChains = () => {
const [selectedAge1, setSelectedAge1] = useState(0);
const [selectedAge2, setSelectedAge2] = useState(0);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
fetchApiData(1);
}, []);
const [openNotif] = useContext(AppContext);
const killedNumber = (item) => {
let killedNumber = "";
killedNumber = item.quantity - item.losses - item.leftOver;
return killedNumber;
};
const fetchApiData = async (page, textValue) => {
setLoading(true);
let response;
if (textValue) {
response = await axios.get(
`poultry_hatching?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&chain=true`
);
} else if (selectedAge1 && selectedAge2) {
response = await axios.get(
`poultry_hatching?role=${getRoleFromUrl()}&age1=${selectedAge1}&age2=${selectedAge2}&page=${page}&page_size=${perPage}&chain=true`
);
} else {
response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&chain=true`
);
}
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
// const updateTable = () => {
// fetchApiData(1);
// };
const handlePerRowsChange = async (newPerPage, page) => {
setLoading(true);
let response;
if (textValue) {
response = await axios.get(
`poultry_hatching?search=filter&value=${textValue}&page=${page}&page_size=${newPerPage}&search=filter&value=${textValue}&chain=true`
);
} else if (selectedAge1 && selectedAge2) {
response = await axios.get(
`poultry_hatching?role=${getRoleFromUrl()}&age1=${selectedAge1}&age2=${selectedAge2}&page=${page}&page_size=${perPage}&chain=true`
);
} else {
response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&page=${page}&page_size=${newPerPage}&chain=true`
);
}
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
};
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&chain=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
const handleRemoveFilter = async (event) => {
event.preventDefault();
setSelectedAge1(null);
setSelectedAge2(null);
setLoading(true);
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);
} finally {
setLoading(false);
}
};
const handleSubmitSearchByAge = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`poultry_hatching?role=${getRoleFromUrl()}&age1=${selectedAge1}&age2=${selectedAge2}&chain=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
let columns = [
// {
// name: "عملیات",
// selector: (item) => (
// <ChainsActiveChainsOperations item={item} updateTable={updateTable} />
// ),
// sortable: false,
// wrap: true,
// allowOverflow: true,
// center: true,
// width: "80px",
// },
{
name: "ردیف",
selector: (item, i) => {
return i + 1;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "30px",
},
{
name: "شرکت زنجیره",
selector: (item, i) => {
return `${item?.chainCompany?.name} (${item?.chainCompany?.user.mobile})`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وضعیت",
selector: (item, i) => {
return (
<Typography
variant="body2"
color={item.violation ? "error" : "black"}
>
{item.violation ? "متخلف" : "عادی"}
</Typography>
);
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "60px",
},
{
name: "نام فارم",
selector: (row) => row.poultry.unitName,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "90px",
},
{
name: "مرغدار",
selector: (item) =>
`${item.poultry.userprofile.fullName} (${item.poultry.userprofile.mobile})`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "شهر/تعاونی",
selector: (item) =>
`${item?.poultry?.address.city.name}/${
item?.poultry?.cityOperator
? item?.poultry?.cityOperator
: "بدون تعاونی"
}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "دامپزشک فارم",
selector: (item) =>
item?.vetFarm?.vetFarmMobile
? `${item?.vetFarm?.vetFarmFullName} (${item?.vetFarm?.vetFarmMobile})`
: "-",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "سالن",
selector: (item) => item.hall,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "60px",
},
{
name: "دوره جوجه ریزی",
selector: (item) => item.period,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تاریخ ثبت جوجه ریزی",
selector: (item) => formatTime(item?.createDate),
sortable: false,
wrap: false,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "تاریخ جوجه ریزی",
selector: (item) => formatTime(item?.date),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "نژاد",
selector: (item) => item.chickenBreed,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "سن",
selector: (row) => row.age,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "60px",
},
{
name: "تعداد جوجه ریزی",
selector: (item) => item?.quantity?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تلفات دوره (قطعه)",
selector: (item) =>
`${item.losses} (%${((item.losses * 100) / item.quantity).toFixed(0)})`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تعداد کل تعهد دولتی (قطعه)",
selector: (item) => `${item?.totalCommitmentQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تعداد کشتار شده دولتی (قطعه)",
selector: (item) => `${item?.governmentalQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن کشتار شده دولتی",
selector: (item) =>
`${item?.governmentalKilledQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تعداد کشتار شده آزاد (قطعه)",
selector: (item) => `${item?.freeQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن کشتار شده آزاد ",
selector: (item) => `${item?.freeKilledQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "حجم خارج از استان",
selector: (item) =>
`${item?.outProvinceKilledQuantity?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن خارج از استان",
selector: (item) => `${item?.outProvinceKilledWeight?.toLocaleString()}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کشتار شده (قطعه)",
selector: (item) => {
return (
killedNumber(item)?.toLocaleString() +
` (%${((killedNumber(item) * 100) / item.quantity).toFixed(0)})`
);
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "90px",
},
{
name: "مانده در سالن (قطعه)",
selector: (item) =>
`${item?.leftOver?.toLocaleString()} (%${(
(item.leftOver * 100) /
item.quantity
).toFixed(0)})`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "90px",
},
{
name: "وزن تعهد دولتی",
selector: (item) => item?.totalCommitment?.toLocaleString() + " کیلوگرم ",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن کشتار دولتی",
selector: (item) =>
item?.governmentalKilledQuantity?.toLocaleString() + " کیلوگرم ",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن کشتار آزاد",
selector: (item) =>
item?.freeKilledQuantity?.toLocaleString() + " کیلوگرم ",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "میانگین وزن کشتار",
selector: (item) =>
item?.totalAverageKilledWeight?.toLocaleString() + " کیلوگرم ",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وزن کل کشتار شده",
selector: (item) =>
item?.totalKilledWeight?.toLocaleString() + " کیلوگرم ",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کشتار فعال",
selector: (row) => (row?.activeKill?.activeKill ? "دارد" : "ندارد"),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تعداد درخواست کشتار",
selector: (item) =>
item?.activeKill?.countOfRequest ? item.activeKill.countOfRequest : "-",
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "آخرین تغییر",
selector: (item) => {
const lastChange =
item.lastChange &&
item.lastChange.date &&
`${item.lastChange.fullName} (${getFaUserRole(
item.lastChange.role
)}) در تاریخ ${formatTime(item.lastChange.date)}`;
return item.lastChange ? lastChange : "-";
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "سازنده جوجه ریزی",
selector: (item) => {
const lastChange =
item.latestHatchingChange &&
item.latestHatchingChange.date &&
`${item.latestHatchingChange.fullName} (${getFaUserRole(
item.latestHatchingChange.role
)}) در تاریخ ${formatTime(item.latestHatchingChange.date)}`;
return item.latestHatchingChange ? lastChange : "-";
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
},
];
const selectAges = Array.from({ length: 75 }, (_, i) => i + 1);
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
mt={2}
>
<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}>جستجو</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}>
{/* <Button
onClick={() => {
dispatch(
DRAWER({
title: "ثبت زنجیره فعال",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <ChainsSubmitActiveChain updateTable={updateTable} />,
})
);
}}
variant="contained"
>
ثبت زنجیره فعال
</Button> */}
<Grid container xs={12} justifyContent="center" mt={2}>
<PageTable
title={tableTitle}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,66 @@
import { Box } from "@mui/system";
import { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { SPACING } from "../../../../data/spacing";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getChainCompanyProfile } from "../../services/get-chain-company-profile";
export const ChainCompanyProfile = () => {
const dispatch = useDispatch();
const [data, setData] = useState();
useEffect(() => {
dispatch(LOADING_START());
dispatch(getChainCompanyProfile()).then((r) => {
setData(r.payload.data);
dispatch(LOADING_END());
});
}, []);
return (
<Box>
<Grid container gap={SPACING.LARGE}>
<Grid container direction="column" xs={12}>
<Grid
container
direction="column"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<>
<Grid>
<SimpleTable
name={`اطلاعات شرکت زنجیره`}
columns={[
"نام",
"نام خانوداگی",
"استان",
"شهر",
"آدرس",
"کدپستی",
]}
data={[
[
data?.user?.firstName,
data?.user?.lastName,
data?.address?.province.name,
data?.address?.city.name,
data?.address?.address,
data?.address?.postalCode,
],
]}
/>
</Grid>
</>
</Grid>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const getChainCompanyProfile = createAsyncThunk(
"CHAIN_GET_PROFILE",
async () => {
const { data, status } = await axios.get("chain-company/0/?profile");
return { data, status };
}
);

View File

@@ -0,0 +1,50 @@
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
import {
ROUTE_CITY_JIHAD_ROUTE_GUILDS,
ROUTE_CITY_JIHAD_ROUTE_STEWARDS,
ROUTE_CITY_POULTRY_ROUTE_GUILDS,
ROUTE_CITY_POULTRY_ROUTE_STEWARDS,
} from "../../../../routes/routes";
import LinkItem from "../../../../components/link-item/LinkItem";
import { MdCorporateFare } from "react-icons/md";
import { IoIosPeople } from "react-icons/io";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const GuildsOperationsCityJihad = () => {
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
>
<NavLink
to={
getRoleFromUrl() === "CityJahad"
? ROUTE_CITY_JIHAD_ROUTE_GUILDS
: ROUTE_CITY_POULTRY_ROUTE_GUILDS
}
>
<LinkItem
icon={<MdCorporateFare size={30} color="#244CCC" />}
title="اصناف"
/>
</NavLink>
<NavLink
to={
getRoleFromUrl() === "CityJahad"
? ROUTE_CITY_JIHAD_ROUTE_STEWARDS
: ROUTE_CITY_POULTRY_ROUTE_STEWARDS
}
>
<LinkItem
icon={<IoIosPeople size={30} color="#244CCC" />}
title="مباشرین"
/>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,61 @@
import { Box } from "@mui/system";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../components/grid/Grid";
import { SimpleTable } from "../../../components/simple-table/SimpleTable";
import { SPACING } from "../../../data/spacing";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { cityVetGetProfileService } from "../services/city-vet-profile";
export const CityVetProfile = () => {
const { cityVetGetProfile } = useSelector((state) => state.generalSlice);
const dispatch = useDispatch();
useEffect(() => {
dispatch(LOADING_START());
dispatch(cityVetGetProfileService()).then((r) => {
dispatch(LOADING_END());
});
}, []);
return (
<Box>
<Grid container gap={SPACING.LARGE}>
<Grid container direction="column" xs={12}>
<Grid
container
direction="column"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<>
<Grid>
<SimpleTable
name={`اطلاعات دامپزشک فارم`}
columns={[
"نام",
"نام خانوداگی",
"استان",
"شهر",
"آدرس",
"کدپستی",
]}
data={[
[
cityVetGetProfile?.user?.firstName,
cityVetGetProfile?.user?.lastName,
cityVetGetProfile?.address?.province.name,
cityVetGetProfile?.address?.city.name,
cityVetGetProfile?.address?.address,
cityVetGetProfile?.address?.postalCode,
],
]}
/>
</Grid>
</>
</Grid>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const cityVetGetProfileService = createAsyncThunk(
"CITY_VET_GET_PROFILE",
async () => {
const { data, status } = await axios.get("city_vet/0/?profile");
return { data, status };
}
);

View File

@@ -0,0 +1,408 @@
import { Button, TextField, Typography } from "@mui/material";
import { useContext, useEffect, useState } from "react";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
// import { ROUTE_CITY_FILE } from "../../../../routes/routes";
// import { avicultureGetRequests } from "../../../aviculture/services/aviculture-requests";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { AppContext } from "../../../../contexts/AppContext";
import { PageTable } from "../../../../components/page-table/PageTable";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import axios from "axios";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
import { formatJustDate } from "../../../../utils/formatTime";
import { IconButton } from "@mui/material";
import { ROUTE_CITY_FILE } from "../../../../routes/routes";
import { useNavigate } from "react-router-dom";
import { RiSearchLine } from "react-icons/ri";
export const CityActiveRequests = () => {
const navigate = useNavigate();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
// page table
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);
let response = await axios.get(
`Poultry_Request/?state=accepted&date1=${selectedDate1}&date2=${selectedDate2}&search=filter&value=${
textValue ? textValue : ""
}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
};
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);
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`Poultry_Request/?state=accepted&date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}&search=filter&value=${
textValue ? textValue : ""
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
const columns = [
{
name: "ردیف",
selector: (item, i) => {
return i + 1;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "40px",
},
{
name: "وضعیت",
selector: (item) => (
<Typography
variant="body2"
color={item.provinceState === "rejected" ? "error" : "promary"}
>
{item.provinceState === "pending"
? "درانتظار تایید استان"
: item.provinceState === "accepted"
? "تایید شده توسط استان"
: "رد شده"}
</Typography>
),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "کدسفارش",
selector: (item) => item.orderCode,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "تاریخ ثبت درخواست",
selector: (item) => formatJustDate(item.createDate),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "نوع کشتار",
selector: (item) => {
return item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی";
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تاریخ کشتار",
selector: (item) => formatJustDate(item.sendDate),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "مرغداری",
selector: (item) =>
`${item?.poultry?.unitName} (${item?.poultry?.user?.mobile})`,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "شهر",
selector: (item) => item?.poultry?.address?.city?.name,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "استان",
selector: (item) => item?.poultry?.address?.province?.name,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "تاریخ جوجه ریزی",
selector: (item) => formatJustDate(item.hatching.hatchingDate),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "سن",
selector: (item) => item.hatching.age,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "تعداد (قطعه)",
selector: (item) => item?.quantity,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "مشاهده",
selector: (item) => {
return (
<IconButton
aria-label="delete"
color="primary"
onClick={() => navigate(ROUTE_CITY_FILE + item?.id)}
>
<PlagiarismIcon />
</IconButton>
);
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
},
];
// useEffect(() => {
// dispatch(LOADING_START());
// dispatch(avicultureGetRequests({ selectedDate1, selectedDate2 })).then(
// (r) => {
// dispatch(LOADING_END());
// }
// );
// }, [selectedDate1, selectedDate2]);
// useEffect(() => {
// const filteredData = avicultureRequests?.filter(
// (item, i) => item.stateProcess === "pending"
// );
// const d = filteredData?.map((item, i) => {
// return [
// i + 1,
// item.orderCode,
// item.poultry.userprofile.baseOrder,
// formatJustDate(item.createDate),
// formatJustDate(item.sendDate),
// item?.process?.poultry?.poultryName,
// item?.process?.poultry?.poultryMobile,
// item?.process?.poultry?.poultryCity,
// item?.process?.poultry?.poultryProvince,
// formatJustDate(item.hatching.date),
// item?.process?.poultry?.age,
// item?.process?.poultry?.poultryQuantity,
// <IconButton
// key={i}
// color="primary"
// onClick={() =>
// dispatch(
// DRAWER({
// right: !(window.innerWidth <= 600),
// bottom: window.innerWidth <= 600,
// content: (
// <CityFileOperations id={item.id} file={item?.process} />
// ),
// title: "انجام عملیات شهرستان",
// })
// )
// }
// >
// <CreateIcon />
// </IconButton>,
// <IconButton
// key={i}
// aria-label="delete"
// color="primary"
// onClick={() =>
// navigate(ROUTE_CITY_FILE + item?.process?.poultry?.poultryRequestId)
// }
// >
// <PlagiarismIcon />
// </IconButton>,
// ];
// });
// setDataTable(d);
// }, [avicultureRequests]);
return (
<>
{/* <AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid container gap={SPACING.TINY}>
<Typography>درخواست های جدید فروش اتحادیه</Typography>
</Grid>
<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>
</Grid>
}
columns={columnsName}
data={dataTable}
/> */}
<Grid alignItems="center" justifyContent="center" mt={4}>
<PageTable
title={
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<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"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
</Grid>
</>
);
};

View File

@@ -0,0 +1,87 @@
import React, { useContext } from "react";
import { useFormik } from "formik";
import { Button, TextField } from "@mui/material";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { archiveHatchingService } from "../../services/archive-hatching";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import * as Yup from "yup";
// import { cityGetHatchingsByAge } from "../../services/city-get-hatchings-by-age";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { cityGetHatchingsByAge } from "../../services/city-get-hatchings-by-age";
const validationSchema = Yup.object({
name: Yup.string(),
});
export const CityArchiveHatchingDrawer = ({
item,
selectedAge1,
selectedAge2,
updateTable,
}) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
name: "",
},
validationSchema,
onSubmit: (values) => {
dispatch(
archiveHatchingService({
key: item.key,
archive_state: "",
message: values.name,
role: getRoleFromUrl(),
})
).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 }));
if (selectedAge1) {
dispatch(cityGetHatchingsByAge({ selectedAge1, selectedAge2 }));
}
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
return (
<form onSubmit={formik.handleSubmit}>
<TextField
id="name"
name="name"
label="توضیحات"
variant="outlined"
multiline
rows={4}
fullWidth
margin="normal"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.name && formik.errors.name}
error={formik.touched.name && Boolean(formik.errors.name)}
/>
<Button type="submit" variant="contained" color="primary" fullWidth>
ثبت
</Button>
</form>
);
};

View File

@@ -0,0 +1,91 @@
import {
Button,
FormControl,
FormHelperText,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useContext } 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 { Yup } from "../../../../lib/yup/yup";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { archiveOldHatchingsService } from "../../services/archive-old-hatchings";
export const CityArchiveOldHatchings = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const validationSchema = Yup.object().shape({
numberField: Yup.number().typeError("عدد وارد کنید").required("اجباری است"),
});
const initialValues = {
numberField: "",
};
const onSubmit = (values) => {
dispatch(
archiveOldHatchingsService({
age: values.numberField,
role: getRoleFromUrl(),
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.msg,
severity: "success",
});
}
});
};
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.TINY}>
<Typography variant="caption" color="error">
توجه : تمام جوجه ریزی های فعالی که بیشتر از سن وارده شده در کادر زیر
باشند به بایگانی منتقل میشوند.
</Typography>
<FormControl
fullWidth
error={formik.touched.numberField && formik.errors.numberField}
>
<TextField
name="numberField"
label="بایگانی کردن جوجه ریزی ها از سن"
type="number"
value={formik.values.numberField}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
{formik.touched.numberField && formik.errors.numberField && (
<FormHelperText>{formik.errors.numberField}</FormHelperText>
)}
</FormControl>
<Button type="submit" variant="contained" color="primary" fullWidth>
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,554 @@
import {
Autocomplete,
Button,
Chip,
Divider,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
} from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import React, { useEffect } from "react";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { useDispatch } from "react-redux";
import { cityGetProvinces } from "../../services/CityGetProvinces";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useState } from "react";
import { cityGetCity } from "../../services/city-get-city";
import { cityEditAvicultureInfo } from "../../services/city-edit-avculture.info";
import { useContext } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { PropTypes } from "prop-types";
import { cityGetPoultryFarm } from "../../services/city-get-poultry-farms";
export const CityEditAvicultureInfoForm = ({ item }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [provinceData, setProvinceData] = useState();
const [cityData, setCityData] = useState();
const [provinceKey, setProvinceKey] = useState();
const [cityKey, setCityKey] = useState();
const [isExistProvince, setIsExistProvince] = useState(true);
const formik = useFormik({
initialValues: {
avicultureName: item?.unitName ? item?.unitName : "",
postal: item?.address.postalCode ? item?.address.postalCode : "",
address: item?.address.address ? item?.address.address : "",
bankUser: item?.userBankInfo?.nameOfBankUser
? item?.userBankInfo?.nameOfBankUser
: "",
card: item?.userBankInfo?.card ? item?.userBankInfo.card : "",
account: item?.userBankInfo?.account ? item?.userBankInfo.account : "",
bankName: "",
shaba: item?.userBankInfo?.shaba ? item?.userBankInfo.shaba : "",
hall: item?.numberOfHalls ? item?.numberOfHalls : "",
breedingUniqueId: item?.breedingUniqueId ? item?.breedingUniqueId : "",
systemCode: item?.systemCode ? item?.systemCode : "",
epidemiologicalCode: item?.epidemiologicalCode
? item?.epidemiologicalCode
: "",
totalCapacity: item?.totalCapacity ? item?.totalCapacity : "",
healthCertificateNumber: item?.healthCertificateNumber
? item?.healthCertificateNumber
: "",
},
validationSchema: Yup.object({
avicultureName: Yup.string().typeError(
"لطفا فیلد را به درستی وارد کنید!"
),
address: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید!"),
card: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
bankUser: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید!"),
postal: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
account: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
bankName: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید!"),
shaba: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید!"),
hall: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
breedingUniqueId: Yup.number().typeError(
"لطفا فیلد را به درستی وارد کنید!"
),
systemCode: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
epidemiologicalCode: Yup.number().typeError(
"لطفا فیلد را به درستی وارد کنید!"
),
totalCapacity: Yup.number().typeError("لطفا فیلد را به درستی وارد کنید!"),
healthCertificateNumber: Yup.number().typeError(
"لطفا فیلد را به درستی وارد کنید!"
),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
dispatch(LOADING_START());
dispatch(cityGetProvinces())?.then((r) => {
dispatch(LOADING_END());
setProvinceData(r.payload.data);
});
}, []);
useEffect(() => {
if (provinceKey) {
dispatch(LOADING_START());
dispatch(cityGetCity(provinceKey)).then((r) => {
setCityData(r.payload.data);
setIsExistProvince(false);
dispatch(LOADING_END());
});
}
}, [provinceKey]);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
display="block"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid>
<TextField
id="breedingUniqueId"
label="شناسه یکتا"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.breedingUniqueId}
error={
formik.touched.breedingUniqueId
? Boolean(formik.errors.breedingUniqueId)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.breedingUniqueId &&
Boolean(formik.errors.breedingUniqueId)
? formik.errors.breedingUniqueId
: null
}
/>
</Grid>
<Grid>
<TextField
id="systemCode"
label="کد سیستمی"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.systemCode}
error={
formik.touched.systemCode
? Boolean(formik.errors.systemCode)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.systemCode && Boolean(formik.errors.systemCode)
? formik.errors.systemCode
: null
}
/>
</Grid>
<Grid>
<TextField
id="epidemiologicalCode"
label="کد اپیدمیولوژیک"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.epidemiologicalCode}
error={
formik.touched.epidemiologicalCode
? Boolean(formik.errors.epidemiologicalCode)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.epidemiologicalCode &&
Boolean(formik.errors.epidemiologicalCode)
? formik.errors.epidemiologicalCode
: null
}
/>
</Grid>
<Grid>
<TextField
id="avicultureName"
label="نام مرغداری"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.avicultureName}
error={
formik.touched.avicultureName
? Boolean(formik.errors.avicultureName)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.avicultureName &&
Boolean(formik.errors.avicultureName)
? formik.errors.avicultureName
: null
}
/>
</Grid>
<Grid>
<Autocomplete
disablePortal
id="province"
options={
provinceData
? provinceData?.map((i) => ({ id: i.key, label: i.name }))
: []
}
onChange={(event, value) => {
setProvinceKey(value.id);
}}
renderInput={(params) => (
<TextField {...params} label="استان را انتخاب کنید" />
)}
/>
</Grid>
<Grid>
<Autocomplete
disabled={isExistProvince}
disablePortal
id="city"
options={
cityData
? cityData.map((i) => ({ id: i.key, label: i.name }))
: []
}
onChange={(event, value) => {
setCityKey(value.id);
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
</Grid>
<Grid>
<TextField
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
id="postal"
label="کد پستی"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.postal}
error={formik.touched.postal ? Boolean(formik.errors.postal) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.postal && Boolean(formik.errors.postal)
? formik.errors.postal
: null
}
/>
</Grid>
<Grid>
<TextField
id="hall"
label="تعداد سالن"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.hall}
error={formik.touched.postal ? Boolean(formik.errors.hall) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.hall && Boolean(formik.errors.hall)
? formik.errors.hall
: null
}
/>
</Grid>
<Grid>
<TextField
id="totalCapacity"
label="ظرفیت کل"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.totalCapacity}
error={
formik.touched.totalCapacity
? Boolean(formik.errors.totalCapacity)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.totalCapacity &&
Boolean(formik.errors.totalCapacity)
? formik.errors.totalCapacity
: null
}
/>
</Grid>
<Grid>
<TextField
id="healthCertificateNumber"
label="شماره گواهی بهداشتی"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.healthCertificateNumber}
error={
formik.touched.healthCertificateNumber
? Boolean(formik.errors.healthCertificateNumber)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.healthCertificateNumber &&
Boolean(formik.errors.healthCertificateNumber)
? formik.errors.healthCertificateNumber
: null
}
/>
</Grid>
<Divider>
<Chip label="اطلاعات بانکی" />
</Divider>
<Grid>
<TextField
id="bankUser"
label="نام صاحب حساب"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.bankUser}
error={
formik.touched.bankUser ? Boolean(formik.errors.bankUser) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.bankUser && Boolean(formik.errors.bankUser)
? formik.errors.bankUser
: null
}
/>
</Grid>
<Grid>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">بانک</InputLabel>
<Select
id="bankName"
label="نام بانک"
value={formik.values.bankName}
error={
formik.touched.bankName ? Boolean(formik.errors.bankName) : null
}
onChange={(e) => {
formik.setFieldValue("bankName", e.target.value);
}}
onBlur={formik.handleBlur}
>
<MenuItem value={"موسسه افضل توس"}>موسسه افضل توس</MenuItem>
<MenuItem value={"انصار"}>انصار</MenuItem>
<MenuItem value={"سپه"}>سپه</MenuItem>
<MenuItem value={"دی"}>دی</MenuItem>
<MenuItem value={"کاب"}>اقتصاد نوین</MenuItem>
<MenuItem value={"گردشگری"}>گردشگری</MenuItem>
<MenuItem value={"حکمت ایرانیان"}>حکمت ایرانیان</MenuItem>
<MenuItem value={"ایران زمین"}>ایران زمین</MenuItem>
<MenuItem value={"کشاورزی"}>کشاورزی</MenuItem>
<MenuItem value={"مسکن"}>مسکن</MenuItem>
<MenuItem value={"مهر ایران"}>مهر ایران</MenuItem>
<MenuItem value={"مهر اقتصاد"}>مهر اقتصاد</MenuItem>
<MenuItem value={"ملت"}>ملت</MenuItem>
<MenuItem value={"ملی"}>ملی</MenuItem>
<MenuItem value={"پارسیان"}>پارسیان</MenuItem>
<MenuItem value={"پاسارگاد"}>پاسارگاد</MenuItem>
<MenuItem value={"پست بانک ایران"}>پست بانک ایران</MenuItem>
<MenuItem value={"صادرات"}>صادرات</MenuItem>
<MenuItem value={"سامان"}>سامان</MenuItem>
<MenuItem value={"صنعت و معدن"}>صنعت و معدن</MenuItem>
<MenuItem value={"سرمایه"}>سرمایه</MenuItem>
<MenuItem value={"شهر"}>شهر</MenuItem>
<MenuItem value={"سینا"}>سینا</MenuItem>
<MenuItem value={"تجارت"}>تجارت</MenuItem>
<MenuItem value={"موسسه اعتباری توسعه"}>
موسسه اعتباری توسعه
</MenuItem>
<MenuItem value={"خاورمیانه"}>خاورمیانه</MenuItem>
</Select>
</FormControl>
</Grid>
<Grid>
<TextField
id="card"
label="شماره کارت"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.card}
error={formik.touched.card ? Boolean(formik.errors.card) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.card && Boolean(formik.errors.card)
? formik.errors.card
: null
}
/>
</Grid>
<Grid>
<TextField
id="account"
label="شماره حساب"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.account}
error={
formik.touched.account ? Boolean(formik.errors.account) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.account && Boolean(formik.errors.account)
? formik.errors.account
: null
}
/>
</Grid>
<Grid>
<TextField
id="shaba"
label="شماره شبا"
variant="outlined"
sx={{ width: "100%" }}
value={formik.values.shaba}
error={formik.touched.shaba ? Boolean(formik.errors.shaba) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.shaba && Boolean(formik.errors.shaba)
? formik.errors.shaba
: null
}
/>
</Grid>
<Grid mb={SPACING.SMALL}>
<Button
fullWidth
variant="contained"
disabled={!formik.validateForm}
onClick={() => {
dispatch(
cityEditAvicultureInfo({
poultry_key: item.key,
breedingUniqueId: formik.values.breedingUniqueId
? formik.values.breedingUniqueId
: null,
systemCode: formik.values.systemCode
? formik.values.systemCode
: null,
epidemiologicalCode: formik.values.epidemiologicalCode
? formik.values.epidemiologicalCode
: null,
totalCapacity: formik.values.totalCapacity
? formik.values.totalCapacity
: null,
healthCertificateNumber: formik.values.healthCertificateNumber
? formik.values.healthCertificateNumber
: null,
address: {
province: provinceKey ? provinceKey : null,
city: cityKey ? cityKey : null,
address: formik.values.address
? formik.values.address
: null,
postal_code: formik.values.postal
? formik.values.postal
: null,
},
user_bank_info: {
name_of_bank_user: formik.values.bankUser
? formik.values.bankUser
: null,
bank_name: formik.values.bankName
? formik.values.bankName
: null,
card: formik.values.card ? formik.values.card : null,
shaba: formik.values.shaba ? formik.values.shaba : null,
account: formik.values.account
? formik.values.account
: null,
},
unit_name: formik.values.avicultureName
? formik.values.avicultureName
: null,
hall: formik.values.hall ? formik.values.hall : null,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است.",
severity: "error",
});
dispatch(LOADING_END());
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
dispatch(cityGetPoultryFarm());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
});
}}
>
ثبت اطلاعات
</Button>
</Grid>
</Grid>
</Grid>
);
};
CityEditAvicultureInfoForm.propTypes = {
item: PropTypes.any,
};

View File

@@ -0,0 +1,110 @@
import { Button, IconButton, TextField, Tooltip } from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import { useDispatch } from "react-redux";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { useContext, useState } from "react";
import { cityEditHatchingQuantityService } from "../../services/city-edit-hatching-quantity";
import { AppContext } from "../../../../contexts/AppContext";
// import { cityGetHatchings } from "../../services/city-get-hatchings";
// import { cityGetHatchingsByAge } from "../../services/city-get-hatchings-by-age";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const CityEditHatchingQuantity = ({
quantity,
hatchingKey,
selectedAge1,
selectedAge2,
updateTable,
}) => {
const dispatch = useDispatch();
return (
<Tooltip title={"ویرایش تعداد جوجه ریزی"} placement="left-start">
<IconButton
size="small"
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تعداد جوجه ریزی",
content: (
<ModalContent
selectedAge1={selectedAge1}
selectedAge2={selectedAge2}
quantity={quantity}
hatchingKey={hatchingKey}
updateTable={updateTable}
/>
),
})
);
}}
>
<EditIcon fontSize="10" />
</IconButton>
</Tooltip>
);
};
const ModalContent = ({
quantity,
hatchingKey,
selectedAge1,
selectedAge2,
updateTable,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [value, setValue] = useState(quantity);
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<>
<TextField
label="تعداد"
type="number"
variant="outlined"
value={value}
onChange={handleChange}
/>
<Button
disabled={!value}
fullWidth
variant="contained"
onClick={() => {
dispatch(
cityEditHatchingQuantityService({
key: hatchingKey,
quantity: Number(value),
role: getRoleFromUrl(),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
// dispatch(cityGetHatchingsByAge({ selectedAge1, selectedAge2 }));
// dispatch(cityGetHatchings({ selectedDate1, selectedDate2 }));
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
ثبت
</Button>
</>
);
};

View File

@@ -0,0 +1,291 @@
import React, { useContext, useEffect } from "react";
import {
Button,
TextField,
Tooltip,
Typography,
Box,
Divider,
Chip,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import { cityGetHatchingInfo } from "../../services/city-get-hatching-info";
import { cityGetHatchingInfoFull } from "../../services/city-get-hatching-info-full";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { AppContext } from "../../../../contexts/AppContext";
import { RiFileExcel2Fill } from "react-icons/ri";
import { useFormik } from "formik";
import axios from "axios";
import { Grid } from "../../../../components/grid/Grid";
export const CityHatchingInfo = () => {
const { hatchingInfoWithDate, hatchingInfoFull } = useSelector(
(state) => state.citySlice
);
const dispatch = useDispatch();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
useEffect(() => {
dispatch(cityGetHatchingInfoFull());
}, []);
useEffect(() => {
dispatch(cityGetHatchingInfo({ selectedDate1, selectedDate2 }));
}, [selectedDate1, selectedDate2]);
const formik = useFormik({
initialValues: {
minAge: "",
maxAge: "",
},
});
return (
<Box bgcolor="#f9f9f9" borderRadius={2} boxShadow={3}>
<Divider style={{ width: "100%" }}>
<Chip
label={
<Typography
variant="body1"
gutterBottom
color="primary"
fontWeight="bold"
>
اطلاعات جوجه ریزی
</Typography>
}
/>
</Divider>
<Grid container spacing={3} m={0} pb={2}>
{/* Full Summary Table */}
<Grid item xs={12}>
<ResponsiveTable
noPagination
title="آمار جوجه ریزی"
columns={[
"تعداد فارم",
"تعداد کل جوجه ریزی",
"تعداد باقی مانده در سالن",
"تعداد کشتار شده",
"وزن کشتار شده",
"مانده سالن (20 تا 30 روزه)",
"مانده سالن (30 تا 40 روزه)",
"مانده سالن (40 تا 50 روزه)",
"مانده سالن (50 تا 60 روزه)",
"بیشتر از 60 روزه",
]}
data={[
[
hatchingInfoFull?.poultries?.toLocaleString(),
hatchingInfoFull?.totalHatchingQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingLeftOverQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingKilledQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingKilledWeight?.toLocaleString(),
hatchingInfoFull?.age2030?.toLocaleString(),
hatchingInfoFull?.age3040?.toLocaleString(),
hatchingInfoFull?.age4050?.toLocaleString(),
hatchingInfoFull?.age5060?.toLocaleString(),
hatchingInfoFull?.ageMoreThan60?.toLocaleString(),
],
]}
/>
</Grid>
<Divider style={{ width: "100%", marginTop: 8 }}>
<Chip
label={
<Typography
variant="body1"
color="primary"
gutterBottom
fontWeight="medium"
>
بر اساس بازه
</Typography>
}
/>
</Divider>
<Grid item xs={12} mt={2}>
<Grid container alignItems="center" spacing={2}>
<Grid item>
<DatePicker
label="از تاریخ"
renderInput={(params) => (
<TextField {...params} size="small" fullWidth />
)}
value={selectedDate1}
onChange={(e) =>
setSelectedDate1(moment(e).format("YYYY-MM-DD"))
}
/>
</Grid>
<Grid item>
<DatePicker
label="تا تاریخ"
renderInput={(params) => (
<TextField {...params} size="small" fullWidth />
)}
value={selectedDate2}
onChange={(e) =>
setSelectedDate2(moment(e).format("YYYY-MM-DD"))
}
/>
</Grid>
<Grid item>
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}hatching_date_range_excel/?date1=${selectedDate1}&date2=${selectedDate2}`}
rel="noreferrer"
>
<Button variant="contained" color="success">
<RiFileExcel2Fill size={24} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
noPagination
title="خلاصه آمار"
columns={[
"تعداد فارم های فعال",
"تعداد کل جوجه ریزی",
"تعداد باقی مانده در سالن",
"تعداد کشتار شده",
"وزن کشتار شده",
]}
data={[
[
hatchingInfoWithDate?.poultries?.toLocaleString(),
hatchingInfoWithDate?.totalHatchingQuantity?.toLocaleString(),
hatchingInfoWithDate?.totalHatchingLeftOverQuantity?.toLocaleString(),
hatchingInfoWithDate?.totalHatchingKilledQuantity?.toLocaleString(),
hatchingInfoWithDate?.totalHatchingKilledWeight?.toLocaleString(),
],
]}
/>
</Grid>
<Divider
style={{
width: "100%",
marginTop: 8,
}}
sx={{
display: { xs: "none", sm: "block" },
}}
>
<Chip
label={
<Typography
variant="body1"
color="primary"
gutterBottom
fontWeight="medium"
mx="auto"
>
گزارش مانده سالن فارم های بیشتر از 10 درصد
</Typography>
}
/>
</Divider>
<Typography
variant="body1"
backgroundColor="rgba(0, 0, 0, 0.08)"
color="primary"
gutterBottom
fontWeight="medium"
mt={4}
mx="auto"
sx={{
display: { xs: "block", sm: "none" },
borderRadius: 2,
}}
width={{
xs: "80%",
sm: "auto",
}}
>
گزارش مانده سالن فارم های بیشتر از 10 درصد
</Typography>
<Grid
item
xs={12}
container
justifyContent="center"
gap={2}
alignItems="center"
mt={4}
px={1}
>
<Grid
container
sx={{
gap: 2,
}}
alignItems="center"
>
<Grid
item
sx={{
maxWidth: { xs: "46%", sm: "100px" },
}}
>
<TextField
id="minAge"
label="از سن"
variant="outlined"
size="small"
value={formik.values.minAge}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</Grid>
<Grid
item
sx={{
maxWidth: { xs: "46%", sm: "100px" },
}}
>
<TextField
id="maxAge"
label="تا سن"
variant="outlined"
size="small"
value={formik.values.maxAge}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
</Grid>
<Grid item>
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}hatching_by_age_range/?min_age=${formik.values.minAge}&max_age=${formik.values.maxAge}`}
rel="noreferrer"
>
<Button variant="contained" color="success">
<RiFileExcel2Fill size={24} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,39 @@
import { useLocation } from "react-router-dom";
import { Button } from "@mui/material";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import {
ROUTE_CITY_HATCHING,
ROUTE_CITY_NEW_REQUEST,
} from "../../../../routes/routes";
export const CityHatchingOperations = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
>
<NavLink
to={ROUTE_CITY_HATCHING}
active={pathname === ROUTE_CITY_HATCHING ? "true" : null}
>
<Button variant="text" color="inherit">
ثبت اطلاعات جوجه ریزی
</Button>
</NavLink>
<NavLink
to={ROUTE_CITY_NEW_REQUEST}
active={pathname === ROUTE_CITY_NEW_REQUEST ? "true" : null}
>
<Button variant="text" color="inherit">
ثبت درخواست کشتار
</Button>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,691 @@
import {
Button,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import axios from "axios";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { formatTime, formatJustDate } from "../../../../utils/formatTime";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { CityManageHatchingsArchiveActions } from "../city-manage-hatchings-operations/CityManageHatchingsOperations";
import { useDispatch, useSelector } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { cityGetHatchingInfoFull } from "../../services/city-get-hatching-info-full";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
// DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import ShowImage from "../../../../components/show-image/ShowImage";
import RemoveRedEyeIcon from "@mui/icons-material/RemoveRedEye";
import ArticleIcon from "@mui/icons-material/Article";
import {
ROUTE_ADMINXـHATCHINGS,
ROUTE_CITY_JIHADـHATCHINGS,
ROUTE_CITY_POULTRYـHATCHINGS,
ROUTE_PROVINCE_SUPERVISORـHATCHINGS,
ROUTE_PROVINCEـHATCHINGS,
ROUTE_SUPER_ADMINـHATCHINGS,
ROUTE_SUPPORTERـHATCHINGS,
} from "../../../../routes/routes";
import { useNavigate } from "react-router-dom";
// import { CityHatchingShowTableDetail } from "../city-hatching-show-table-detail/CityHatchingShowTableDetail";
export const CityHatchingUnassigned = ({ readOnly }) => {
const dispatch = useDispatch();
const isReadOnly = readOnly || false;
const [selectedAge1, setSelectedAge1] = useState(0);
const [selectedAge2, setSelectedAge2] = useState(0);
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 userKey = useSelector((state) => state.userSlice.userProfile.key);
const navigate = useNavigate();
const [openNotif] = useContext(AppContext);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const hatchingAdded = useSelector((state) => state.citySlice.hatchingAdded);
useEffect(() => {
fetchApiData();
}, [hatchingAdded]);
const fetchApiData = async (pageParam = page, perPageParam = perPage) => {
dispatch(LOADING_START());
const response = await axios.get("poultry_hatching/", {
params: {
unknown: true,
search: "filter",
value: textValue,
role: getRoleFromUrl(),
page: pageParam,
page_size: perPageParam,
age1: selectedAge1 || 0,
age2: selectedAge2 || 0,
},
});
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
setPage(page);
fetchApiData(page, perPage);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
useEffect(() => {
fetchApiData();
}, [perPage]);
const updateTable = () => {
fetchApiData();
};
// const killedNumber = (item) => {
// let killedNumber = "";
// killedNumber = item.quantity - item.losses - item.leftOver;
// return killedNumber;
// };
useEffect(() => {
const d = data?.map((item, i) => {
return [
<CityManageHatchingsArchiveActions
selectedAge1={null}
selectedAge2={null}
updateTable={updateTable}
item={item}
key={"archive-actions"}
readOnly={isReadOnly}
/>,
<Tooltip placement="left" title="جزئیات جوجه ریزی" key={item?.key}>
<IconButton
color="primary"
onClick={() => {
navigate(
getRoleFromUrl() === "AdminX"
? `${ROUTE_ADMINXـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "Supporter"
? `${ROUTE_SUPPORTERـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "SuperAdmin"
? `${ROUTE_SUPER_ADMINـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "CityPoultry"
? `${ROUTE_CITY_POULTRYـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "ProvinceSupervisor"
? `${ROUTE_PROVINCE_SUPERVISORـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "ProvinceOperator"
? `${ROUTE_PROVINCEـHATCHINGS}/${item.key}`
: getRoleFromUrl() === "CityJahad"
? `${ROUTE_CITY_JIHADـHATCHINGS}/${item.key}`
: ""
);
}}
>
<RemoveRedEyeIcon />
</IconButton>
</Tooltip>,
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
<Tooltip
disableHoverListener={
!(item?.killingInfo?.violationMessage && item?.violation)
}
key={item?.key}
title={
item?.violation
? `متن گزارش تخلف: ${item?.killingInfo?.violationMessage}`
: null
}
sx={{
"&:hover": {
cursor: item?.violation ? "pointer" : "default",
},
}}
placement="top"
>
<Typography
variant="body2"
color={item?.violation ? "error" : "primary"}
>
{item?.violation ? "پیگیری" : "عادی"}
</Typography>
</Tooltip>,
item?.licenceNumber,
item?.poultry?.breedingUniqueId,
item?.CertId,
// item?.commitmentType === "free" ? "آزاد" : "دولتی",
item?.poultry?.unitName || "-",
`${item?.poultry?.userprofile?.fullName ?? "-"} (${
item?.poultry?.userprofile?.mobile ?? "-"
}) ${item?.violationReport ? "✉️" : ""}`,
item?.InteractTypeName ? (
<Typography
variant="body2"
color={item?.hasTenant ? "success.main" : ""}
fontWeight={item?.hasTenant ? "bold" : "normal"}
>
{item?.InteractTypeName}
</Typography>
) : (
"-"
),
item?.PersonTypeName,
item?.UnionTypeName,
`${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?.poultry?.killingAveAge?.toLocaleString(),
item?.predicateDate ? formatJustDate(item?.predicateDate) : "-",
item.chickenBreed,
item.age,
item?.quantity?.toLocaleString(),
item?.increaseQuantity?.toLocaleString(),
`${item.losses} (%${((item.losses * 100) / item.quantity).toFixed(0)})`,
<Tooltip
key={item?.key}
placement="top"
title="جهت مشاهده ثبت کننده تلفات کلیک کنید"
>
<Button
// variant="outlined"
style={{ color: "rgba(0,0,0,0.87)" }}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تعداد جوجه ریزی",
content: (
<Grid container xs={12}>
{!item?.directLossesInputer &&
!item?.directLossesInputer ? (
<Typography variant="body1">
برای این جوجه ریزی تلفاتی ثبت نشده است.
</Typography>
) : (
<Grid xs={12}>
<Typography variant="body1">
ثبت کننده تلفات اتحادیه:{" "}
{item?.directLossesInputer
? `${
item?.directLossesInputer
} در تاریخ ${formatJustDate(
item?.directLossesDate
)}`
: " - "}
</Typography>
<Typography variant="body1">
ویرایش کننده تلفات اتحادیه:
{item?.directLossesEditor
? `${
item?.directLossesEditor
} در تاریخ ${formatJustDate(
item?.directLossesLastEditDate
)}`
: " - "}
</Typography>
</Grid>
)}
</Grid>
),
})
);
}}
>
{`${item?.directLosses?.toLocaleString()} (%${(
(item.directLosses * 100) /
item.quantity
).toFixed(0)})`}
</Button>
</Tooltip>,
`${item?.totalLosses?.toLocaleString()} (%${(
(item.totalLosses * 100) /
item.quantity
).toFixed(0)})`,
`${item?.totalCommitmentQuantity?.toLocaleString()}`,
`${item?.totalFreeCommitmentQuantity?.toLocaleString()}`,
`${item?.governmentalQuantity?.toLocaleString()}`,
`${item?.governmentalKilledQuantity?.toLocaleString()}`,
`${item?.freeQuantity?.toLocaleString()}`,
`${item?.freeKilledQuantity?.toLocaleString()}`,
`${item?.outProvinceKilledQuantity?.toLocaleString()}`,
`${item?.outProvinceKilledWeight?.toLocaleString()}`,
`${item?.barDifferenceRequestQuantity?.toLocaleString()}`,
`${item?.barDifferenceRequestWeight?.toLocaleString()}`,
`${item?.killingInfo?.provinceKillRequests?.toLocaleString()}`,
`${item?.killingInfo?.provinceKillRequestsQuantity?.toLocaleString()}`,
`${item?.killingInfo?.provinceKillRequestsWeight?.toLocaleString()}`,
item?.killedQuantity?.toLocaleString() +
` (%${((item?.killedQuantity * 100) / item.quantity).toFixed(0)})`,
item?.leftOver?.toLocaleString(),
`%${((item?.leftOver * 100) / item?.quantity).toFixed(0)}`,
// item?.totalCommitment?.toLocaleString(),
`%${((item.totalLosses * 100) / item.quantity).toFixed(0)}`,
`%${((item?.killedQuantity * 100) / item.quantity).toFixed(0)}`,
`%${((item?.leftOver * 100) / item?.quantity).toFixed(0)}`,
`%${(
((item?.killedQuantity + item?.totalLosses) * 100) /
item?.quantity
).toFixed(0)}`,
item?.samasatDischargePercentage
? `%${item?.samasatDischargePercentage}`
: "-",
item?.totalCommitment?.toLocaleString(),
item?.governmentalKilledQuantity?.toLocaleString(),
item?.freeKilledQuantity?.toLocaleString(),
item?.totalAverageKilledWeight?.toLocaleString(),
item?.totalKilledWeight?.toLocaleString(),
item?.activeKill?.activeKill ? "دارد" : "ندارد",
item?.activeKill?.countOfRequest ? item.activeKill.countOfRequest : "-",
item?.killingInfo?.killHouseRequests?.toLocaleString(),
item?.killingInfo?.killHouseRequestsFirstQuantity?.toLocaleString(),
item?.killingInfo?.killHouseRequestsFirstWeight?.toLocaleString(),
item?.killingInfo?.barCompleteWithKillHouse?.toLocaleString(),
item?.killingInfo?.acceptedRealWightFinal?.toLocaleString(),
item?.chainKilledQuantity?.toLocaleString(),
item?.chainKilledWeight?.toLocaleString(),
item?.exportKilledQuantity?.toLocaleString(),
item?.exportKilledWeight?.toLocaleString(),
item?.killingInfo?.wareHouseBars?.toLocaleString(),
item?.killingInfo?.wareHouseBarsQuantity?.toLocaleString(),
item?.killingInfo?.wareHouseBarsWeight?.toLocaleString(),
item?.killingInfo?.wareHouseBarsWeightLose?.toFixed(2),
item.lastChange
? `${item.lastChange.fullName} (${getFaUserRole(
item.lastChange.role
)}) در تاریخ ${formatTime(item.lastChange.date)}`
: "-",
item.latestHatchingChange
? `${item.latestHatchingChange.fullName} (${getFaUserRole(
item.latestHatchingChange.role
)}) در تاریخ ${formatTime(item.latestHatchingChange.date)}`
: "-",
item?.violationReport ? (
<Tooltip title="مشاهده گزارش" placement="top" key={item?.key}>
<IconButton
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "گزارش ",
content: (
<SimpleTable
columns={[
"ثبت کننده",
"تاریخ ثبت",
"تخلف",
"متن گزارش",
"سند",
]}
data={[
[
item?.violationReporter,
formatJustDate(item?.violationReportDate),
item?.violation ? "دارد" : "ندارد",
item?.violationReport,
<Grid
key={item?.key}
container
xs={12}
justifyContent="center"
gap={1}
>
{item?.violationImage?.map((option, index) => (
<ShowImage
key={`${option}-${index}`}
src={option}
/>
))}
</Grid>,
],
]}
/>
),
})
);
}}
>
<ArticleIcon />
</IconButton>
</Tooltip>
) : (
"-"
),
];
});
setTableData(d);
}, [data]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
dispatch(
cityGetHatchingInfoFull({
age1: selectedAge1,
age2: selectedAge2,
tab: "unknown",
textValue: textValue,
})
);
try {
const response = await axios.get(
`poultry_hatching/?role=${getRoleFromUrl()}&age1=${
selectedAge1 ? selectedAge1 : 0
}&age2=${
selectedAge2 ? selectedAge2 : 0
}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}&unknown=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(0);
setSelectedAge2(0);
dispatch(LOADING_START());
setTextValue("");
dispatch(
cityGetHatchingInfoFull({
age1: 0,
age2: 0,
tab: "unknown",
textValue: textValue,
})
);
try {
const response = await axios.get(
`poultry_hatching?role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}&search=filter&value=${textValue}&unknown=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
};
const [lastUpdateData, setLastUpdateData] = useState();
useEffect(() => {
async function fetchData() {
try {
const response = await axios.get(`last_update/?type=poultry_hatching`);
setLastUpdateData(response.data);
} catch (error) {
console.error("Error fetching data:", error);
}
}
fetchData();
}, []);
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
xs={12}
mt={2}
>
<form onSubmit={handleSubmit} style={{ flex: 1 }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid sx={{ width: { xs: "72px", sm: "80px" } }}>
<TextField
size="small"
label="از سن"
id="outlined-controlled"
value={selectedAge1}
onChange={(event) => {
setSelectedAge1(event.target.value);
}}
/>
</Grid>
<Grid sx={{ width: { xs: "72px", sm: "80px" } }}>
<TextField
size="small"
label="تا سن"
id="outlined-controlled"
value={selectedAge2}
onChange={(event) => {
setSelectedAge2(event.target.value);
}}
/>
</Grid>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
sx={{ maxWidth: { xs: "100%", sm: 250 } }}
value={textValue}
onChange={handleTextChange}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleSubmit(e);
}
}}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
<Tooltip title="خروجی اکسل" px={0}>
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}0/hatching_excel/?unknown=true&role=${getRoleFromUrl()}&key=${userKey}&age1=${
selectedAge1 ? selectedAge1 : 0
}&age2=${
selectedAge2 ? selectedAge2 : 0
}&search=filter&value=${textValue}`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
</Tooltip>
</Grid>
</form>
<Button onClick={handleRemoveFilter} color="error">
حذف فیلتر
</Button>
</Grid>
);
const { hatchingInfoFull } = useSelector((state) => state.citySlice);
useEffect(() => {
dispatch(
cityGetHatchingInfoFull({
age1: selectedAge1,
age2: selectedAge2,
tab: "unknown",
textValue: textValue,
})
);
}, []);
return (
<Grid alignItems="center" justifyContent="center" mt={2} xs={12}>
<Grid alignItems="center" justifyContent="center" isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
title={
lastUpdateData &&
`آخرین بروزرسانی : ${formatTime(lastUpdateData)} ${" "}`
}
columns={[
"تعداد فارم",
"تعداد جوجه ریزی",
"حجم کل جوجه ریزی",
"حجم باقی مانده در سالن",
"حجم کشتار شده",
"وزن کشتار شده",
"حجم کل تلفات",
"حجم تلفات اتحادیه",
"حجم تلفات دامپزشک",
"مانده سالن ( 20 تا 30 روزه)",
"مانده سالن ( 30 تا 40 روزه)",
"مانده سالن ( 40 تا 50 روزه)",
"مانده سالن ( 50 تا 60 روزه)",
"بیشتر از 60 روزه",
]}
data={[
[
hatchingInfoFull?.poultries?.toLocaleString(),
hatchingInfoFull?.hatchings?.toLocaleString(),
hatchingInfoFull?.totalHatchingQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingLeftOverQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingKilledQuantity?.toLocaleString(),
hatchingInfoFull?.totalHatchingKilledWeight?.toLocaleString(),
hatchingInfoFull?.totalHatchingAllLosses?.toLocaleString(),
hatchingInfoFull?.totalHatchingUnionLosses?.toLocaleString(),
hatchingInfoFull?.totalHatchingVetLosses?.toLocaleString(),
hatchingInfoFull?.age2030?.toLocaleString(),
hatchingInfoFull?.age3040?.toLocaleString(),
hatchingInfoFull?.age4050?.toLocaleString(),
hatchingInfoFull?.age5060?.toLocaleString(),
hatchingInfoFull?.ageMoreThan60?.toLocaleString(),
],
]}
/>
</Grid>
{tableTitle}
<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