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,335 @@
import {
Autocomplete,
Button,
FormControl,
InputAdornment,
InputLabel,
MenuItem,
Select,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AppContext } from "../../../contexts/AppContext";
import { Yup } from "../../../lib/yup/yup";
import { CLOSE_MODAL } from "../../../lib/redux/slices/appSlice";
import { SPACING } from "../../../data/spacing";
import { Grid } from "../../../components/grid/Grid";
import { guildGetStewardsService } from "../services/guild-get-guilds";
import { guildGetAllocationData } from "../services/guildGetAllocationData";
import { guildGetStewards } from "../services/guild-get-stewards";
import { slaughterAllocateStewardService } from "../../slaughter-house/services/slaughter-allocate-steward";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { GuildEditAllocateStewardService } from "../services/guildEditAllocateStewardService";
import { NumberInput } from "../../../components/number-format-custom/NumberFormatCustom";
export const GuildAddSteward = ({
stewardKey,
guildKey,
sellType,
isGuild,
weight,
quantity,
item,
totalAverageWeightOfCarcasses,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const [price, setPrice] = useState();
const { guildGetStewardsState } = useSelector((state) => state.generalSlice);
useEffect(() => {
if (sellType !== "exclusive") {
dispatch(guildGetStewardsService());
}
}, []);
const getOptionLabel = (option) => option.label;
const validationSchema = Yup.object({
slaughteSteward: Yup.string(),
// price: Yup.number().when("quantity", {
// is: quantity,
// then: Yup.number().required("لطفا این فیلد را پر کنید"),
// otherwise: Yup.number(),
// }),
wholePrice: Yup.number().when("quantity", {
is: quantity,
then: Yup.number().required("لطفا این فیلد را پر کنید"),
otherwise: Yup.number(),
}),
numberOfPieces: Yup.number()
.required("حجم لاشه را وارد کنید")
.min(1, "حداقل یک قطعه"),
weight: Yup.number()
.required("وزن لاشه را وارد کنید")
.min(0.01, "حداقل 0.01 کیلوگرم"),
});
let defaultKey;
if (isGuild) {
defaultKey = guildKey;
} else {
defaultKey = stewardKey;
}
const formik = useFormik({
initialValues: {
slaughteSteward: defaultKey,
numberOfPieces: "",
wholePrice: "",
weight: weight ? weight : "",
},
validationSchema,
onSubmit: (values) => {
let req;
if (quantity) {
dispatch(
GuildEditAllocateStewardService({
steward_allocation_key: item.key,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
})
).then(() => {
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
dispatch(guildGetStewards({ date: selectedDate1 }));
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
});
} else {
if (isGuild) {
req = {
guild_key: values.slaughteSteward,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
sell_type: sellType,
date: selectedDate1,
broadcast_type: "steward_broadcast",
amount: Number(price),
total_amount: Number(values.wholePrice),
};
} else {
req = {
guild_key: values.slaughteSteward,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
sell_type: sellType,
date: selectedDate1,
broadcast_type: "steward_broadcast",
amount: Number(price),
total_amount: Number(values.wholePrice),
};
}
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(guildGetStewards({ date: selectedDate1 }));
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
},
});
useEffect(() => {
const finalQuantity = formik.values.weight / totalAverageWeightOfCarcasses;
formik.setFieldValue("numberOfPieces", Number(finalQuantity).toFixed(0));
}, [formik.values.weight]);
useEffect(() => {
const calcPrice = formik.values.wholePrice / Number(formik.values.weight);
setPrice(Number(calcPrice.toFixed(0)));
}, [formik.values.wholePrice]);
return (
<form onSubmit={formik.handleSubmit}>
<Grid container width="100%" gap={SPACING.SMALL}>
{!stewardKey && !guildKey && !isGuild && !quantity && (
<Autocomplete
disablePortal
options={guildGetStewardsState}
getOptionLabel={getOptionLabel}
sx={{ width: "100%" }}
size="small"
onChange={(event, value) =>
formik.setFieldValue("slaughteSteward", value.value)
}
onBlur={formik.handleBlur("slaughteSteward")}
// value={formik.values.slaughteSteward}
renderInput={(params) => <TextField {...params} label="مباشر" />}
/>
)}
{isGuild && !quantity && !stewardKey && !guildKey && (
<Autocomplete
disablePortal
options={guildGetStewardsState}
getOptionLabel={getOptionLabel}
sx={{ width: "100%" }}
size="small"
onChange={(event, value) =>
formik.setFieldValue("slaughteSteward", value.value)
}
onBlur={formik.handleBlur("slaughteSteward")}
// value={formik.values.slaughteSteward}
renderInput={(params) => <TextField {...params} label="اصناف" />}
/>
)}
<TextField
id="weight"
name="weight"
type="number"
label="وزن لاشه"
variant="outlined"
size="small"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
{...formik.getFieldProps("weight")}
error={formik.touched.weight && formik.errors.weight}
helperText={formik.touched.weight && formik.errors.weight}
/>
{!quantity && (
<>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
size="small"
id="wholePrice"
label="هزینه کل"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">ریال</InputAdornment>
),
}}
value={formik.values.wholePrice}
error={
formik.touched.wholePrice
? Boolean(formik.errors.wholePrice)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.wholePrice && Boolean(formik.errors.wholePrice)
? formik.errors.wholePrice
: null
}
/>
<FormControl fullWidth>
<InputLabel id="demo-select-label">خودرو حمل لاشه</InputLabel>
<Select
labelId="demo-select-label"
id="demo-select"
value=""
disabled
label="Select Option"
size="small"
>
{/* You can add MenuItem components for each option */}
<MenuItem value="" disabled>
Select an option
</MenuItem>
<MenuItem value={10}>Option 1</MenuItem>
<MenuItem value={20}>Option 2</MenuItem>
<MenuItem value={30}>Option 3</MenuItem>
</Select>
</FormControl>
<NumberInput
allowLeadingZeros
thousandSeparator=","
size="small"
fullWidth
id="price"
label="قیمت هر کیلو مرغ"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">ریال</InputAdornment>
),
readOnly: true,
}}
value={price}
/>
</>
)}
<TextField
id="numberOfPieces"
name="numberOfPieces"
type="number"
label="حجم لاشه"
size="small"
variant="outlined"
value={formik.values.numberOfPieces}
InputProps={{
endAdornment: <InputAdornment position="end">قطعه</InputAdornment>,
}}
{...formik.getFieldProps("numberOfPieces")}
error={formik.touched.numberOfPieces && formik.errors.numberOfPieces}
helperText={
formik.touched.numberOfPieces && formik.errors.numberOfPieces
}
/>
<Button variant="contained" fullWidth type="submit">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,778 @@
import {
Button,
Collapse,
IconButton,
List,
ListItem,
ListItemText,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import axios from "axios";
import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
import { AppContext } from "../../../contexts/AppContext";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { guildGetStewards } from "../services/guild-get-stewards";
import { formatJustDate } from "../../../utils/formatTime";
import { SPACING } from "../../../data/spacing";
import { Grid } from "../../../components/grid/Grid";
import { SimpleTable } from "../../../components/simple-table/SimpleTable";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../lib/redux/slices/appSlice";
import { GuildAddSteward } from "./GuildAddSteward";
import { guildGetAllocationData } from "../services/guildGetAllocationData";
import { GuildManageInventoryAllocationOperations } from "./GuildManageInventoryAllocationOperations";
import { guildInventoryFinalSubmitService } from "../services/guildInventoryFinalSubmitService";
import BoxList from "../../../components/box-list/BoxList";
import { RespSlaughterGuildListItem } from "../../slaughter-house/components/resp-slaughter-guild-list-item/RespSlaughterGuildListItem";
import { RespGuildExclusiveItem } from "../../slaughter-house/components/resp-guild-exclusive-item/RespGuildExclusiveItem";
import { SlaughterSubmitOutProvinceSell } from "../../slaughter-house/components/slaughter-submit-out-province-sell/SlaughterSubmitOutProvinceSell";
import { guildGetFreeSaleBarService } from "../services/guild-get-free-sale-bar";
import { guildDeleteOutOfProvinceSell } from "../services/guild-delete-free-sale";
export const GuildBroadCastManagement = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1, setSelectedDate1] = useContext(AppContext);
const [stewardsDataTable, setStewardsDataTable] = useState([]);
const [inventoryAllocationsDataTable, setInventoryAllocationsDataTable] =
useState([]);
const {
guildGetInventoryStock,
guildStewards,
guildGetGuildData,
guildFreeSaleBars,
} = useSelector((state) => state.generalSlice);
const [outOfProvinceSells, setOutOfProvinceSells] = useState([]);
useEffect(() => {
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
).then(() => {
dispatch(
guildGetFreeSaleBarService({
date: selectedDate1,
steward_key: guildGetInventoryStock?.stewardKey,
})
);
});
dispatch(guildGetStewards({ date: selectedDate1 }));
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
}, [selectedDate1]);
useEffect(() => {
const inventoryAllocationsData = guildGetGuildData?.map((item, i) => {
let sellType, sellerType;
if (item.sellerType === "guilds") {
sellerType = "صنف";
} else if (item.sellerType === "steward") {
sellerType = "مباشر";
}
if (item.sellType === "free") {
sellType = "آزاد";
} else {
if (item.type === "manual") {
sellType = "اختصاصی (دستی)";
} else {
sellType = "اختصاصی (اتوماتیک)";
}
}
return [
i + 1,
item?.guilds?.guildsId,
formatJustDate(item.date),
sellerType,
sellType,
item?.guilds?.guildsName,
item?.guilds?.user?.fullname,
item?.guilds?.user?.nationalId,
item?.guilds?.user?.mobile,
item?.guilds?.typeActivity,
item?.guilds?.areaActivity,
item?.guilds?.licenseNumber,
item?.guilds?.address?.city.name,
item?.numberOfCarcasses,
item?.weightOfCarcasses,
item?.loggedRegistrationCode ? item.loggedRegistrationCode : "-",
<GuildManageInventoryAllocationOperations item={item} key={i} />,
];
});
setInventoryAllocationsDataTable(inventoryAllocationsData);
const d1 = guildStewards?.map((item, i) => {
return [
item?.guildsId,
"صنف",
item.guildsName,
item.user.fullname,
item.user.nationalId,
item.user.mobile,
item.typeActivity,
item.areaActivity,
item.licenseNumber,
item.address.city.name,
item?.allocationLimit
? `${item?.allocationLimit?.toLocaleString()} کیلوگرم`
: "بدون محدودیت",
<Button
key={i}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تخصیص به صنف",
size: 300,
content: (
<GuildAddSteward
sellType={"exclusive"}
key={item.key + i}
isGuild={false}
stewardKey={item.key}
totalAverageWeightOfCarcasses={
guildGetInventoryStock?.totalAverageWeightOfCarcasses
}
/>
),
})
);
}}
>
تخصیص
</Button>,
];
});
setStewardsDataTable(d1);
const sells = guildFreeSaleBars?.map((item, i) => {
return [
i + 1,
formatJustDate(item?.createDate),
item?.buyerName,
item?.buyerMobile,
item?.province,
item?.city,
item?.driverName,
item?.driverMobile,
item?.driverName,
item?.clearanceCode,
item?.numberOfCarcasses,
item?.weightOfCarcasses,
<>
<IconButton
key={i}
aria-label="delete"
color="primary"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش فروش خارج از استان",
content: (
<SlaughterSubmitOutProvinceSell
stewardKey={guildGetInventoryStock?.stewardKey}
isEdit={true}
item={item}
selectedDate={selectedDate1}
/>
),
})
);
}}
>
<EditIcon />
</IconButton>
<IconButton
key={i}
aria-label="delete"
color="secondary"
onClick={() => {
dispatch(guildDeleteOutOfProvinceSell(item?.key)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
guildGetFreeSaleBarService({
date: selectedDate1,
steward_key: guildGetInventoryStock?.stewardKey,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "success",
});
}
});
}}
>
<DeleteIcon />
</IconButton>
</>,
];
});
setOutOfProvinceSells(sells);
}, [guildStewards, guildGetGuildData, guildFreeSaleBars]);
const finalSubmitDisabled =
(Array.isArray(guildGetGuildData) && !guildGetGuildData?.length) ||
(Array.isArray(guildGetGuildData) &&
guildGetGuildData[0]?.finalRegistration);
const [inventoryDate, setInventoryDate] = useState(new Date(selectedDate1));
useEffect(() => {
const tempDate = new Date(inventoryDate);
tempDate.setDate(new Date(selectedDate1).getDate() - 1);
setInventoryDate(tempDate);
}, [selectedDate1]);
const [isMessageVisible, setMessageVisible] = useState(true);
const toggleMessageVisibility = () => {
setMessageVisible(!isMessageVisible);
};
const isMobile = window.innerWidth <= 600;
const [value, setValue] = useState("");
const [searchFilter, setSearchFilter] = useState([]);
const handleChange = (event) => {
setValue(event.target.value);
const regex = new RegExp(value, "i");
const d = stewardsDataTable.filter((item) => regex.test(item));
setSearchFilter(d);
};
return (
<Grid
container
gap={SPACING.SMALL}
alignItems="center"
justifyContent="start"
width="100%"
>
<Typography variant="h6">مدیریت پخش</Typography>
<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 width="100%">
{isMobile ? (
<Grid p={SPACING.TINY}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid container alignItems="center">
<Typography>موجودی انبار</Typography>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}Broadcast_management/?date=${selectedDate1}&kill_house_key=${"AAA"}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
<Grid>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "فروش آزاد",
content: (
<SlaughterSubmitOutProvinceSell
stewardKey={guildGetInventoryStock?.stewardKey}
selectedDate={selectedDate1}
/>
),
})
);
}}
>
فروش آزاد
</Button>
</Grid>
</Grid>
<BoxList
columns={[
"حجم لاشه ها",
"وزن لاشه ها (کیلوگرم)",
// "تعداد تخصیصات",
"حجم لاشه تخصیص داده شده",
"وزن تخصیص داده شده (کیلوگرم)",
"حجم لاشه قابل تخصیص",
"وزن قابل تخصیص (کیلوگرم)",
"وزن بار فروش آزاد (کیلوگرم)",
"حجم بار فروش آزاد (قطعه)",
]}
data={[
[
guildGetInventoryStock?.realNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.realWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.allocatedTotalNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.allocatedTotalWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.remainTotalNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.remainTotalWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.freeSaleWeight?.toLocaleString(),
guildGetInventoryStock?.freeSaleQuantity?.toLocaleString(),
],
]}
/>
</Grid>
) : (
<SimpleTable
name={
<Grid
container
alignItems="center"
justifyContent="space-between"
>
<Grid container alignItems="center">
<Typography>موجودی انبار</Typography>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}Broadcast_management/?date=${selectedDate1}&kill_house_key=${"AAA"}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
<Grid>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "فروش آزاد",
content: (
<SlaughterSubmitOutProvinceSell
stewardKey={guildGetInventoryStock?.stewardKey}
selectedDate={selectedDate1}
/>
),
})
);
}}
>
فروش آزاد
</Button>
</Grid>
</Grid>
}
columns={[
"حجم لاشه ها",
"وزن لاشه ها (کیلوگرم)",
// "تعداد تخصیصات",
"حجم لاشه تخصیص داده شده",
"وزن تخصیص داده شده (کیلوگرم)",
"حجم لاشه قابل تخصیص",
"وزن قابل تخصیص (کیلوگرم)",
"وزن بار فروش آزاد (کیلوگرم)",
"حجم بار فروش آزاد (قطعه)",
]}
data={[
[
guildGetInventoryStock?.realNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.realWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.allocatedTotalNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.allocatedTotalWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.remainTotalNumberOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.remainTotalWeightOfCarcasses?.toLocaleString(),
guildGetInventoryStock?.freeSaleWeight?.toLocaleString(),
guildGetInventoryStock?.freeSaleQuantity?.toLocaleString(),
],
]}
/>
)}
</Grid>
<Grid width="100%">
<List>
<ListItem
sx={{ backgroundColor: "#dfe6e9", cursor: "pointer" }}
onClick={toggleMessageVisibility}
>
<ListItemText primary={"صنف اختصاصی"} />
<Button variant="text" onClick={toggleMessageVisibility}>
{isMessageVisible ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</Button>
</ListItem>
<Collapse in={isMessageVisible}>
<Grid mt={SPACING.TINY}>
<TextField
label="جستجو ..."
variant="outlined"
size="small"
onChange={handleChange}
fullWidth
/>
</Grid>
{isMobile ? (
<RespSlaughterGuildListItem
data={value ? searchFilter : stewardsDataTable}
/>
) : (
<ListItem>
<SimpleTable
columns={[
"شناسه صنف",
"ماهیت",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"موبایل",
"نوع فعالیت",
"حوزه فعالیت",
"شماره مجوز",
"شهرستان",
"میانگین سهم",
"عملیات",
]}
data={value ? searchFilter : stewardsDataTable}
/>
</ListItem>
)}
</Collapse>
</List>
</Grid>
{isMobile ? (
<>
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
justifyContent="space-between"
>
<Typography>مدیریت کل تخصیصات</Typography>
<Grid container gap={SPACING.SMALL} alignItems="center">
<Button
// disabled={!guildGetGuildData?.length}
variant="outlined"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تخصیص آزاد به صنف",
content: (
<GuildAddSteward
isGuild
sellType="free"
totalAverageWeightOfCarcasses={
guildStewards.totalAverageWeightOfCarcasses
}
/>
),
})
);
}}
>
تخصیص آزاد به صنف
</Button>
<Button
variant="contained"
disabled={finalSubmitDisabled}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت نهایی",
content: (
<Grid container gap={SPACING.SMALL}>
<Typography>
در صورت ثبت نهایی انجام هیچگونه عملیاتی مانند حذف و
ویرایش امکان پذیر نمی باشد.
</Typography>
<Grid
container
direction="column"
gap={SPACING.TINY}
width="100%"
>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
guildInventoryFinalSubmitService({
steward_guild_allocation_list:
guildGetGuildData.map((item) => item.key),
})
).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: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
ارسال یکجا کداحراز
</Button>
</Grid>
</Grid>
<RespGuildExclusiveItem data={inventoryAllocationsDataTable} />
{!!outOfProvinceSells?.length && (
<Grid mt={2}>
<BoxList
name={"فروش خارج از استان"}
columns={[
"ردیف",
"تاریخ",
"نام خریدار",
"تلفن",
"استان",
"شهر",
"نام راننده",
"تلفن راننده",
"نوع خودرو",
"کد قرنطینه",
"حجم لاشه",
"وزن لاشه",
"عملیات",
]}
data={outOfProvinceSells}
/>
</Grid>
)}
</>
) : (
<Grid width="100%">
<SimpleTable
name={
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
justifyContent="space-between"
>
<Typography>مدیریت کل تخصیصات</Typography>
<Grid container gap={SPACING.SMALL} alignItems="center">
<Button
// disabled={!guildGetGuildData?.length}
variant="outlined"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تخصیص آزاد به صنف",
content: (
<GuildAddSteward
isGuild
sellType="free"
totalAverageWeightOfCarcasses={
guildGetInventoryStock?.totalAverageWeightOfCarcasses
}
/>
),
})
);
}}
>
تخصیص آزاد به صنف
</Button>
<Button
variant="contained"
disabled={finalSubmitDisabled}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت نهایی",
content: (
<Grid container gap={SPACING.SMALL}>
<Typography>
در صورت ثبت نهایی انجام هیچگونه عملیاتی مانند
حذف و ویرایش امکان پذیر نمی باشد.
</Typography>
<Grid
container
direction="column"
gap={SPACING.TINY}
width="100%"
>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
guildInventoryFinalSubmitService({
steward_guild_allocation_list:
guildGetGuildData.map(
(item) => item.key
),
})
).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: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
ارسال یکجا کداحراز
</Button>
</Grid>
</Grid>
}
columns={[
"ردیف",
"شناسه صنف",
"تاریخ ثبت",
"ماهیت",
"نوع تخصیص",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"موبایل",
"نوع فعالیت",
"حوزه فعالیت",
"شماره مجوز",
"شهرستان",
"حجم لاشه",
"وزن لاشه",
"کداحراز",
"عملیات",
]}
data={inventoryAllocationsDataTable}
/>
{!!outOfProvinceSells?.length && (
<Grid mt={2}>
<SimpleTable
name={"فروش خارج از استان"}
columns={[
"ردیف",
"تاریخ",
"نام خریدار",
"تلفن",
"استان",
"شهر",
"نام راننده",
"تلفن راننده",
"نوع خودرو",
"کد قرنطینه",
"حجم لاشه",
"وزن لاشه",
"عملیات",
]}
data={outOfProvinceSells}
/>
</Grid>
)}
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,78 @@
import { VscNewFolder } from "react-icons/vsc";
import { useLocation } from "react-router-dom";
import { Grid } from "../../../components/grid/Grid";
import LinkItem from "../../../components/link-item/LinkItem";
import { NavLink } from "../../../components/nav-link/NavLink";
import { SPACING } from "../../../data/spacing";
import {
ROUTE_STEWARD_SALE_IN_PROVINCE,
ROUTE_STEWARD_INVENTORY_STOCK,
ROUTE_STEWARD_PURCHASE_OUT_PROVINCE,
ROUTE_STEWARD_SALE_OUT_PROVINCE,
ROUTE_STEWARD_SEGMENT,
} from "../../../routes/routes";
export const GuildInventoryOperation = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "row", md: "row" }}
justifyContent="center"
// style={{ placeContent: "baseline" }}
xs={12}
alignItems="center"
>
<NavLink
to={ROUTE_STEWARD_INVENTORY_STOCK}
active={pathname === ROUTE_STEWARD_INVENTORY_STOCK ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="ورود به انبار"
/>
</NavLink>
<NavLink
to={ROUTE_STEWARD_SALE_IN_PROVINCE}
active={pathname === ROUTE_STEWARD_SALE_IN_PROVINCE ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="فروش داخل استان"
/>
</NavLink>
<NavLink
to={ROUTE_STEWARD_PURCHASE_OUT_PROVINCE}
active={
pathname === ROUTE_STEWARD_PURCHASE_OUT_PROVINCE ? "true" : null
}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="خرید خارج استان"
/>
</NavLink>
<NavLink
to={ROUTE_STEWARD_SALE_OUT_PROVINCE}
active={pathname === ROUTE_STEWARD_SALE_OUT_PROVINCE ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="فروش به خارج استان"
/>
</NavLink>
<NavLink
to={ROUTE_STEWARD_SEGMENT}
active={pathname === ROUTE_STEWARD_SEGMENT ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="قطعه بندی"
/>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,415 @@
import {
Button,
Pagination,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import axios from "axios";
import { useEffect, useState } from "react";
import { RiFileExcel2Fill } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import { RiSearchLine } from "react-icons/ri";
import { ManageGuildsOperations } from "../../province/components/manage-guilds-operations/ManageGuildsOperations";
import { Grid } from "../../../components/grid/Grid";
import { SPACING } from "../../../data/spacing";
import { CreateGuilds } from "../../province/components/create-guilds/CreateGuilds";
import { PageTable } from "../../../components/page-table/PageTable";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import BoxList from "../../../components/box-list/BoxList";
const GuildMaangeGuilds = () => {
const dispatch = useDispatch();
const [dataTableM, setDataTableM] = useState([]);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
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(
`total_guilds/?role=Guilds&steward=true&search=filter&value=${textValue}&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(
`total_guilds/?role=Guilds&steward=true&search=filter&value=${textValue}&page=${page}&page_size=${newPerPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
dispatch(LOADING_END());
};
const [page, setPage] = useState(0);
const handleChangePageM = (event, newPage) => {
dispatch(LOADING_START());
setPage(newPage);
fetchApiData(newPage + 1, textValue);
};
useEffect(() => {
fetchApiData(1);
}, []);
const updateTable = () => {
fetchApiData(1);
};
const columns = [
{
name: "شناسه صنف",
selector: (item) => item.guildsId,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "نام واحد صنفی",
selector: (item) => item?.guildsName,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "نام شخص/شرکت",
selector: (item) => `${item?.user?.fullname} (${item?.user?.mobile})`,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "کدملی",
selector: (item) => item?.user?.nationalId,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "نوع فعالیت",
selector: (item) => item?.typeActivity,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "حوزه فعالیت",
selector: (item) => item?.areaActivity,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کدپستی",
selector: (item) => item?.address?.postalCode,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "استان/شهر/آدرس",
selector: (item) =>
`${item?.address?.province.name}/${item?.address?.city.name}/${item?.address?.address}`,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "مباشر",
selector: (item) => (item?.steward ? "می باشد" : "نمی باشد"),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "محدودیت تخصیص",
selector: (item) => (item?.limitationAllocation ? "دارد" : "ندارد"),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "حداکثر تخصیص",
selector: (item) => item?.allocationLimit,
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "مباشر",
selector: (item) =>
item?.centersAllocation?.map((item) => item.label).join(" - "),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کشتارگاه",
selector: (item) => {
return item?.killHouseInfo
?.map((item) => `${item.name} (${item.mobile})`)
.join(" - ");
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "وضعیت",
selector: (item) => {
let state = "";
if (item?.provinceAcceptState === "accepted") {
state = "تایید شده";
} else if (item?.provinceAcceptState === "rejected") {
state = "رد شده";
} else if (item?.provinceAcceptState === "pending") {
state = "در انتظار تایید";
}
return state;
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "عملیات",
selector: (item, i) => (
<ManageGuildsOperations
key={i + item?.guildsId}
guild={item}
updateTable={updateTable}
/>
),
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "110px",
},
];
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`total_guilds/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&steward=true`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
if (getRoleFromUrl() === "CityJahad") {
columns.pop();
}
const getItemState = (item) => {
let state = "";
if (item?.provinceAcceptState === "accepted") {
state = "تایید شده";
} else if (item?.provinceAcceptState === "rejected") {
state = "رد شده";
} else if (item?.provinceAcceptState === "pending") {
state = "در انتظار تایید";
}
return state;
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
item.guildsId,
item?.guildsName,
`${item?.user?.fullname} (${item?.user?.mobile})`,
item?.user?.nationalId,
item?.typeActivity,
item?.areaActivity,
item?.address?.postalCode,
`${item?.address?.province.name}/${item?.address?.city.name}/${item?.address?.address}`,
item?.steward ? "می باشد" : "نمی باشد",
item?.limitationAllocation ? "دارد" : "ندارد",
item?.allocationLimit,
item?.centersAllocation?.map((item) => item.label).join(" - "),
item?.killHouseInfo
?.map((item) => `${item.name} (${item.mobile})`)
.join(" - "),
getItemState(item),
<ManageGuildsOperations key={i + item?.guildsId} guild={item} />,
];
});
setDataTableM(d);
}, [data]);
const columnNames = columns.map((column) => column.name);
const isMobile = window.innerWidth <= 600;
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Grid
container
width="100%"
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>مدیریت اصناف</Typography>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
<Grid>
{getRoleFromUrl() !== "CityJahad" && (
<Button
variant="contained"
onClick={() => {
dispatch(
OPEN_MODAL({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
size: window.innerWidth <= 600 ? "small" : "auto",
title: "ثبت واحد جدید",
content: <CreateGuilds updateTable={updateTable} />,
})
);
}}
>
ثبت واحد جدید
</Button>
)}
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}guilds_excel/?key=${userKey}&role=${getRoleFromUrl()}&search=filter&value=${textValue}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
</Grid>
);
return (
<Grid>
{isMobile ? (
<Grid container justifyContent="center" gap={SPACING.SMALL}>
{tableTitle}
<BoxList columns={columnNames} data={dataTableM} />
<Pagination
count={Math.ceil(totalRows / 10)}
page={page + 1}
variant="outlined"
onChange={(event, newPage) => {
handleChangePageM(event, newPage - 1);
}}
/>
</Grid>
) : (
<PageTable
title={tableTitle}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
)}
</Grid>
);
};
export default GuildMaangeGuilds;

View File

@@ -0,0 +1,197 @@
import { Button, Typography } from "@mui/material";
import { useDispatch } from "react-redux";
import { GuildAddSteward } from "./GuildAddSteward";
import { Grid } from "../../../components/grid/Grid";
import {
CLOSE_MODAL,
LOADING_END,
OPEN_MODAL,
} from "../../../lib/redux/slices/appSlice";
import { guildGetAllocationData } from "../services/guildGetAllocationData";
import { guildGetStewards } from "../services/guild-get-stewards";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { AppContext } from "../../../contexts/AppContext";
import { useContext } from "react";
import { guildDeleteAllocatedService } from "../services/guildDeleteAllocatedService";
import { SPACING } from "../../../data/spacing";
import { guildInventoryFinalSubmitService } from "../services/guildInventoryFinalSubmitService";
import { GuildSubmitAuthCode } from "./GuildSubmitAuthCode";
export const GuildManageInventoryAllocationOperations = ({ item }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
// const { inventorySelectedKillHouse } = useSelector(
// (state) => state.slaughterSlice
// );
return (
<Grid container direction="column">
{item.systemRegistrationCode && (
<Button
size="small"
disabled={item.finalRegistration || item.loggedRegistrationCode}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "کداحراز",
content: <GuildSubmitAuthCode item={item} />,
})
);
}}
>
ثبت کداحراز
</Button>
)}
{!item.systemRegistrationCode && (
<Button
size="small"
disabled={item.finalRegistration}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ارسال کداحراز",
content: (
<Grid container gap={SPACING.SMALL}>
<Typography>
در صورت ارسال کداحراز انجام هیچگونه عملیاتی مانند حذف و
ویرایش امکان پذیر نمی باشد.
</Typography>
<Grid
container
direction="column"
gap={SPACING.TINY}
width="100%"
>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
guildInventoryFinalSubmitService({
steward_guild_allocation_list: [item.key],
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
ارسال کداحراز
</Button>
)}
{!item.systemRegistrationCode && (
<>
<Button
size="small"
disabled={item.finalRegistration}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص",
content: (
<GuildAddSteward
stewardKey={item.key}
item={item}
quantity={item?.numberOfCarcasses}
weight={item?.weightOfCarcasses}
/>
),
})
);
}}
>
ویرایش
</Button>
<Button
size="small"
disabled={item.finalRegistration}
color="error"
onClick={() => {
dispatch(
guildDeleteAllocatedService({
steward_allocation_key: item.key,
})
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
dispatch(guildGetStewards({ date: selectedDate1 }));
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
حذف
</Button>
</>
)}
</Grid>
);
};

View File

@@ -0,0 +1,67 @@
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 { guildGetProfile } from "../services/guild-get-profile";
export const GuildProfile = () => {
const { guildProfile } = useSelector((state) => state.generalSlice);
const dispatch = useDispatch();
useEffect(() => {
dispatch(LOADING_START());
dispatch(guildGetProfile()).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={[
[
guildProfile?.guildsName,
guildProfile?.guildsId,
guildProfile?.licenseNumber,
guildProfile?.areaActivity,
guildProfile?.typeActivity,
guildProfile?.centersAllocation
?.map((item) => item.label)
.join(" - "),
guildProfile?.killHouseCentersAllocation
?.map((item) => item.label)
.join(" - "),
],
]}
/>
</Grid>
</>
</Grid>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,317 @@
import {
Button,
Checkbox,
Divider,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { Grid } from "../../../components/grid/Grid";
import { SPACING } from "../../../data/spacing";
import { Yup } from "../../../lib/yup/yup";
import { useFormik } from "formik";
import { guildAllocatedStockOperationService } from "../services/guild-allocated-stock-operation";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../contexts/AppContext";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { guildGetInventoryAllocatedService } from "../services/guild-get-inventory-allocated";
import { CLOSE_MODAL } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { senfGetInventoryStockService } from "../services/senf-get-inventory-stock";
import { senfGetInventoryAllocatedService } from "../services/senf-get-inventory-allocated";
export const GuildReceiveBarOperation = ({ item }) => {
const dispatch = useDispatch();
const [, , selectedDate1] = useContext(AppContext);
const [openNotif] = useContext(AppContext);
const [selectedValue, setSelectedValue] = useState("option1");
const handleChange = (event) => {
setSelectedValue(event.target.value);
};
const initialValues = {
authCode: "",
};
const validationSchema = Yup.object({
authCode: Yup.string().required("کداحراز اجباری است"),
});
const onSubmit = () => {
// Handle form submission here
// console.log("Form submitted with values:", values);
};
const formik = useFormik({
initialValues,
validationSchema,
onSubmit,
});
const [isChecked, setChecked] = useState(false);
const handleCheckboxChange = () => {
setChecked(!isChecked);
};
const formikRealInfo = useFormik({
initialValues: {
number: "",
weight: "",
},
validationSchema: Yup.object({
weight: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
formikRealInfo.validateForm();
}, []);
return (
<Grid
container
width="100%"
direction="column"
justifyContent="space-between"
gap={SPACING.TINY}
>
<Grid container justifyContent="space-around">
<Typography variant="caption">
وزن تخصیصی: {item.weightOfCarcasses.toLocaleString()} کیلوگرم
</Typography>
<Typography variant="caption">
تعداد تخصیصی: {item.numberOfCarcasses.toLocaleString()} قطعه
</Typography>
</Grid>
<Divider />
<Grid container spacing={1} alignItems="center">
<Grid item>
<FormControlLabel
control={
<Checkbox
size="small"
checked={isChecked}
onChange={handleCheckboxChange}
/>
}
label="ثبت تعداد واقعی تحویلی"
/>
</Grid>
<Grid container xs={12} spacing={1}>
<Grid item xs={6}>
<TextField
id="weight"
size="small"
label="وزن"
variant="outlined"
fullWidth
disabled={!isChecked}
onChange={formikRealInfo.handleChange}
onBlur={formikRealInfo.handleBlur}
value={formikRealInfo.values.weight}
error={
formikRealInfo.touched.weight &&
Boolean(formikRealInfo.errors.weight)
}
helperText={
formikRealInfo.touched.weight
? formikRealInfo.errors.weight
: ""
}
/>
</Grid>
</Grid>
</Grid>
<FormControl component="fieldset">
<RadioGroup
row
aria-label="radio-buttons-group"
name="radio-buttons-group"
value={selectedValue}
onChange={handleChange}
style={{ justifyContent: "space-between", flexDirection: "row" }}
>
<FormControlLabel
value="option1"
control={<Radio />}
label="تحویل بار با کداحراز"
/>
<FormControlLabel
value="option2"
control={<Radio />}
label="تحویل بار بدون کداحراز"
/>
</RadioGroup>
</FormControl>
{selectedValue === "option1" && (
<TextField
fullWidth
id="authCode"
name="authCode"
label="کداحراز"
variant="outlined"
margin="normal"
onChange={formik.handleChange}
onBlur={formik.handleBlur} // Add onBlur to track touched state
value={formik.values.authCode}
error={formik.touched.authCode && Boolean(formik.errors.authCode)}
helperText={formik.touched.authCode ? formik.errors.authCode : ""}
/>
)}
<Button
disabled={
(selectedValue === "option1" ? !formik.isValid : false) ||
(isChecked ? !formikRealInfo.isValid : false)
}
variant="contained"
onClick={() => {
let reqObj = {};
if (formik.values.authCode) {
if (getRoleFromUrl() === "senf") {
reqObj = {
guild_check_allocation: true,
receiver_real_number_of_carcasses: 0,
receiver_real_weight_of_carcasses: formikRealInfo.values.weight
? formikRealInfo.values.weight
: item.weightOfCarcasses,
allocation_key: item.key,
registration_code: Number(formik.values.authCode),
state: "accepted",
};
} else {
reqObj = {
steward_check_allocation: true,
allocation_key: item.key,
receiver_real_number_of_carcasses: 0,
receiver_real_weight_of_carcasses: formikRealInfo.values.weight
? formikRealInfo.values.weight
: item.weightOfCarcasses,
registration_code: Number(formik.values.authCode),
state: "accepted",
};
}
} else {
if (getRoleFromUrl() === "senf") {
reqObj = {
guild_check_allocation: true,
allocation_key: item.key,
receiver_real_number_of_carcasses: 0,
receiver_real_weight_of_carcasses: formikRealInfo.values.weight
? formikRealInfo.values.weight
: item.weightOfCarcasses,
state: "accepted",
};
} else {
reqObj = {
steward_check_allocation: true,
allocation_key: item.key,
receiver_real_number_of_carcasses: 0,
receiver_real_weight_of_carcasses: formikRealInfo.values.weight
? formikRealInfo.values.weight
: item.weightOfCarcasses,
state: "accepted",
};
}
}
dispatch(guildAllocatedStockOperationService(reqObj)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
if (getRoleFromUrl() === "senf") {
dispatch(
senfGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(
senfGetInventoryAllocatedService({
date: selectedDate1,
})
);
} else {
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryAllocatedService({
date: selectedDate1,
})
);
}
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
تحویل بار
</Button>
<Button
variant="contained"
color="error"
onClick={() => {
dispatch(
guildAllocatedStockOperationService({
steward_check_allocation: true,
allocation_key: item.key,
state: "rejected",
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryAllocatedService({
date: selectedDate1,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
رد بار
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,87 @@
import { Button, TextField } from "@mui/material";
import { useFormik } from "formik";
import { useContext } from "react";
import { useDispatch } from "react-redux";
import { Yup } from "../../../lib/yup/yup";
import { AppContext } from "../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { guildGetAllocationData } from "../services/guildGetAllocationData";
import { Grid } from "../../../components/grid/Grid";
import { SPACING } from "../../../data/spacing";
import { guildAuthCodeSubmitService } from "../services/guild-auth-code-submit";
const validationSchema = Yup.object({
phoneNumber: Yup.string()
.matches(/^\d+$/, "فقط عدد مجاز است")
.required("کداحراز اجباری است"),
});
export const GuildSubmitAuthCode = ({ item }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const formik = useFormik({
initialValues: {
phoneNumber: "",
},
validationSchema: validationSchema,
onSubmit: (values) => {
dispatch(
guildAuthCodeSubmitService({
logged_registration_code: values.phoneNumber,
steward_allocation_key: item.key,
role: getRoleFromUrl(),
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
guildGetAllocationData({
date: selectedDate1,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid container width="100%" direction="column" gap={SPACING.SMALL}>
<TextField
fullWidth
id="phoneNumber"
name="phoneNumber"
label="کداحراز"
variant="outlined"
value={formik.values.phoneNumber}
error={
formik.touched.phoneNumber && Boolean(formik.errors.phoneNumber)
}
helperText={formik.touched.phoneNumber && formik.errors.phoneNumber}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<Button type="submit" fullWidth variant="contained">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,380 @@
import { Button, InputAdornment, TextField, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { useFormik } from "formik";
// import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { Yup } from "../../../lib/yup/yup";
import { AppContext } from "../../../contexts/AppContext";
import { fixBase64 } from "../../../utils/toBase64";
import { Grid } from "../../../components/grid/Grid";
import { SPACING } from "../../../data/spacing";
import { ImageUpload } from "../../../components/image-upload/ImageUpload";
import { guildSubmitFreeBar } from "../services/guild-submit-free-bar";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { guildGetInventoryAllocatedService } from "../services/guild-get-inventory-allocated";
import { DRAWER } from "../../../lib/redux/slices/appSlice";
import { guildGetFreeBars } from "../services/guild-get-free-bars";
import { guildEditFreeBar } from "../services/guild-edit-free-bar";
const ValidationSchema = Yup.object().shape({
// kill_house_key: Yup.string().required("کد خانه کشتار الزامی است"),
slaughter_name: Yup.string().required("نام کشتارگاه الزامی است"),
slaughter_mobile: Yup.string()
.required("شماره موبایل کشتارگاه الزامی است")
.min(11, "شماره موبایل باید دقیقاً 11 رقم باشد")
.max(11, "شماره موبایل باید دقیقاً 11 رقم باشد"),
province: Yup.string().required("استان الزامی است"),
city: Yup.string().required("شهر الزامی است"),
vet_farm_name: Yup.string().required("نام دامپزشک مربوطه الزامی است"),
vet_farm_mobile: Yup.string()
.required("شماره موبایل دامپزشک مربوطه الزامی است")
.min(11, "شماره موبایل باید دقیقاً 11 رقم باشد")
.max(11, "شماره موبایل باید دقیقاً 11 رقم باشد"),
driver_name: Yup.string().required("نام راننده الزامی است"),
driver_mobile: Yup.string()
.required("شماره موبایل راننده الزامی است")
.min(11, "شماره موبایل باید دقیقاً 11 رقم باشد")
.max(11, "شماره موبایل باید دقیقاً 11 رقم باشد"),
car: Yup.string().required("نوع خودرو الزامی است"),
number_of_carcasses: Yup.number()
.required("حجم لاشه الزامی است")
.min(1, "حجم لاشه باید بیشتر از 0 باشد"),
weight_of_carcasses: Yup.number()
.required("وزن لاشه الزامی است")
.min(0.01, "وزن باید بیشتر از 0 باشد"),
bar_image: Yup.string(),
selectedDate: Yup.date().required("تاریخ الزامی است"),
});
export const GuildSubmitFreeBar = ({ guildKey, item, editMode }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
// const { inventorySelectedKillHouse } = useSelector(
// (state) => state.slaughterSlice
// );
const [, , selectedDate1] = useContext(AppContext);
const thenCallback = (r) => {
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(
guildGetInventoryAllocatedService({
date: selectedDate1,
})
);
dispatch(
guildGetFreeBars({
date: selectedDate1,
})
);
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
};
const [profileImages, setProfileImages] = useState([]);
const formik = useFormik({
initialValues: {
kill_house_key: "",
slaughter_name: item ? item.killHouseName : "",
slaughter_mobile: item ? item.killHouseMobile : "",
province: item ? item.province : "",
city: item ? item.city : "",
vet_farm_name: item ? item.killHouseVetName : "",
vet_farm_mobile: item ? item.killHouseVetMobile : "",
driver_name: item ? item.driverName : "",
driver_mobile: item ? item.driverMobile : "",
car: item ? item.car : "",
number_of_carcasses: item ? item.numberOfCarcasses : "",
weight_of_carcasses: item ? item.weightOfCarcasses : "",
bar_image: "",
selectedDate: item ? new Date(item.date) : new Date(),
},
validationSchema: ValidationSchema,
});
useEffect(() => {
formik.validateForm();
if (item?.barImage) {
factorPaymentHandler([{ data_url: item?.barImage }]);
}
}, []);
const factorPaymentHandler = (imageList, addUpdateIndex) => {
if (imageList[0]) {
formik.setFieldValue("bar_image", fixBase64(imageList[0]?.data_url));
}
setProfileImages(imageList);
};
return (
<Grid container direction="column">
<form
style={{
display: "flex",
flexDirection: "column",
gap: SPACING.LARGE,
}}
>
<DatePicker
disabled={editMode}
label="تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "100%" }} {...params} />
)}
value={formik.values.selectedDate}
onChange={(e) => {
formik.setFieldValue("selectedDate", new Date(e));
}}
/>
<TextField
id="slaughter_name"
name="slaughter_name"
label="نام کشتارگاه"
value={formik.values.slaughter_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.slaughter_name &&
Boolean(formik.errors.slaughter_name)
}
helperText={
formik.touched.slaughter_name && formik.errors.slaughter_name
}
/>
<TextField
id="slaughter_mobile"
name="slaughter_mobile"
label="تلفن کشتارگاه"
value={formik.values.slaughter_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.slaughter_mobile &&
Boolean(formik.errors.slaughter_mobile)
}
helperText={
formik.touched.slaughter_mobile && formik.errors.slaughter_mobile
}
/>
<TextField
id="province"
name="province"
label="استان"
value={formik.values.province}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.province && Boolean(formik.errors.province)}
helperText={formik.touched.province && formik.errors.province}
/>
<TextField
id="city"
name="city"
label="شهر"
value={formik.values.city}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
/>
<TextField
id="vet_farm_name"
name="vet_farm_name"
label="نام دامپزشک کشتارگاه"
value={formik.values.vet_farm_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.vet_farm_name && Boolean(formik.errors.vet_farm_name)
}
helperText={
formik.touched.vet_farm_name && formik.errors.vet_farm_name
}
/>
<TextField
id="vet_farm_mobile"
name="vet_farm_mobile"
label="تلفن دامپزشک کشتارگاه"
value={formik.values.vet_farm_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.vet_farm_mobile &&
Boolean(formik.errors.vet_farm_mobile)
}
helperText={
formik.touched.vet_farm_mobile && formik.errors.vet_farm_mobile
}
/>
<TextField
id="driver_name"
name="driver_name"
label="نام راننده"
value={formik.values.driver_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.driver_name && Boolean(formik.errors.driver_name)
}
helperText={formik.touched.driver_name && formik.errors.driver_name}
/>
<TextField
id="driver_mobile"
name="driver_mobile"
label="تلفن راننده"
value={formik.values.driver_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.driver_mobile && Boolean(formik.errors.driver_mobile)
}
helperText={
formik.touched.driver_mobile && formik.errors.driver_mobile
}
/>
<TextField
id="car"
name="car"
label="خودرو"
value={formik.values.car}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.car && Boolean(formik.errors.car)}
helperText={formik.touched.car && formik.errors.car}
/>
<TextField
id="number_of_carcasses"
name="number_of_carcasses"
label="حجم لاشه"
InputProps={{
endAdornment: <InputAdornment position="end">قطعه</InputAdornment>,
}}
value={formik.values.number_of_carcasses}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.number_of_carcasses &&
Boolean(formik.errors.number_of_carcasses)
}
helperText={
formik.touched.number_of_carcasses &&
formik.errors.number_of_carcasses
}
/>
<TextField
id="weight_of_carcasses"
name="weight_of_carcasses"
label="وزن لاشه"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
value={formik.values.weight_of_carcasses}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.weight_of_carcasses &&
Boolean(formik.errors.weight_of_carcasses)
}
helperText={
formik.touched.weight_of_carcasses &&
formik.errors.weight_of_carcasses
}
/>
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"تصویر بار"}
/>
{formik.touched.driver_mobile &&
Boolean(formik.errors.driver_mobile) && (
<Typography color="error">ثبت تصویر بار الزامی است</Typography>
)}
<Button
disabled={!formik.isValid}
onClick={() => {
if (editMode) {
dispatch(
guildEditFreeBar({
key: guildKey,
kill_house_name: formik.values.slaughter_name,
kill_house_mobile: formik.values.slaughter_mobile,
kill_house_vet_name: formik.values.vet_farm_name,
kill_house_vet_mobile: formik.values.vet_farm_mobile,
province: formik.values.province,
city: formik.values.city,
driver_name: formik.values.driver_name,
driver_mobile: formik.values.driver_mobile,
car: formik.values.car,
number_of_carcasses: formik.values.number_of_carcasses,
weight_of_carcasses: formik.values.weight_of_carcasses,
bar_image: formik.values.bar_image,
date: formik.values.selectedDate,
})
).then(thenCallback);
} else {
dispatch(
guildSubmitFreeBar({
steward_key: guildKey,
kill_house_name: formik.values.slaughter_name,
kill_house_mobile: formik.values.slaughter_mobile,
kill_house_vet_name: formik.values.vet_farm_name,
kill_house_vet_mobile: formik.values.vet_farm_mobile,
province: formik.values.province,
city: formik.values.city,
driver_name: formik.values.driver_name,
driver_mobile: formik.values.driver_mobile,
car: formik.values.car,
number_of_carcasses: formik.values.number_of_carcasses,
weight_of_carcasses: formik.values.weight_of_carcasses,
bar_image: formik.values.bar_image,
date: formik.values.selectedDate,
})
).then(thenCallback);
}
}}
fullWidth
variant="contained"
color="primary"
>
{editMode ? "ویرایش" : "ثبت"}
</Button>
</form>
</Grid>
);
};

View File

@@ -0,0 +1,5 @@
import { GuildBroadCastManagement } from "./GuildBroadCastManagement";
export const GuildBroadcast = () => {
return <GuildBroadCastManagement />;
};

View File

@@ -0,0 +1,143 @@
import { useFormik } from "formik";
import { Grid } from "../../../components/grid/Grid";
import { Button, InputAdornment, TextField } from "@mui/material";
import { Yup } from "../../../lib/yup/yup";
import { useContext, useEffect } from "react";
import { SPACING } from "../../../data/spacing";
import { useDispatch, useSelector } from "react-redux";
import { guildUpdateAllocatedStockService } from "../services/guild-update-allocated-stock";
import { AppContext } from "../../../contexts/AppContext";
import { guildGetInventoryStockService } from "../services/guild-get-inventory-stock";
import { guildGetInventoryAllocatedService } from "../services/guild-get-inventory-allocated";
import { CLOSE_MODAL } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { senfGetInventoryStockService } from "../services/senf-get-inventory-stock";
import { senfGetInventoryAllocatedService } from "../services/senf-get-inventory-allocated";
import { checkPathStartsWith } from "../../../utils/checkPathStartsWith";
const schema = Yup.object().shape({
quantity: Yup.number().required("وارد کردن تعداد اجباری است"),
weight: Yup.number().required("وارد کردن وزن اجباری است"),
});
export const RegisterEditDeliveryNumberAndWeight = ({ item }) => {
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const formik = useFormik({
initialValues: {
quantity: item?.receiverRealNumberOfCarcasses,
weight: item?.receiverRealWeightOfCarcasses,
},
validationSchema: schema,
onSubmit: (values) => {
dispatch(
guildUpdateAllocatedStockService({
steward: true,
allocation_key: item.key,
receiver_real_number_of_carcasses: Number(values.quantity),
receiver_real_weight_of_carcasses: Number(values.weight),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
if (getRoleFromUrl() === "senf") {
dispatch(
senfGetInventoryStockService({
date: selectedDate1,
})
);
dispatch(
senfGetInventoryAllocatedService({
date: selectedDate1,
})
);
} else {
dispatch(
guildGetInventoryStockService({
date: selectedDate1,
role_key: checkPathStartsWith("senf")
? selectedSubUser?.key
: "",
})
);
dispatch(
guildGetInventoryAllocatedService({
date: selectedDate1,
role_key: checkPathStartsWith("senf")
? selectedSubUser?.key
: "",
})
);
}
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
},
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
const quantity = formik.values.weight / item.indexWeight;
formik.setFieldValue("quantity", quantity.toFixed(0));
}, [formik.values.weight]);
return (
<Grid container direction="column" width="100%">
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.SMALL}>
<TextField
id="weight"
name="weight"
label="وزن واقعی تحویلی"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
value={formik.values.weight}
onChange={formik.handleChange}
error={formik.touched.weight && formik.errors.weight}
helperText={formik.touched.weight && formik.errors.weight}
/>
<TextField
id="quantity"
name="quantity"
label="تعداد واقعی تحویلی"
disabled={true}
InputProps={{
endAdornment: (
<InputAdornment position="end">قطعه</InputAdornment>
),
}}
value={formik.values.quantity}
onChange={formik.handleChange}
error={formik.touched.quantity && formik.errors.quantity}
helperText={formik.touched.quantity && formik.errors.quantity}
/>
<Button type="submit" fullWidth variant="contained">
ثبت
</Button>
</Grid>
</form>
</Grid>
);
};

View File

@@ -0,0 +1,890 @@
import React, { useContext, useEffect, useState, useCallback } from "react";
import {
Autocomplete,
Button,
Checkbox,
FormControl,
FormControlLabel,
InputAdornment,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { useFormik } from "formik";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { AppContext } from "../../../contexts/AppContext";
import { provincePolicyGetUploadImageService } from "../../province/services/province-policy-upload-image";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { slaughterGetProductsService } from "../../slaughter-house/services/slaughter-inventory-gets";
import { slaughterGetGuildsForAllocateService } from "../../slaughter-house/services/slaughter-get-guilds-for-allocate";
import { Yup } from "../../../lib/yup/yup";
import { fixBase64 } from "../../../utils/toBase64";
import { CLOSE_MODAL, DRAWER } from "../../../lib/redux/slices/appSlice";
import { NumberInput } from "../../../components/number-format-custom/NumberFormatCustom";
import { ImageUpload } from "../../../components/image-upload/ImageUpload";
import { Grid } from "../../../components/grid/Grid";
import {
slaughterAllocateStewardService,
slaughterEditAllocateStewardService,
} from "../../slaughter-house/services/slaughter-allocate-steward";
import { fetchSlaughterBroadcastAndProducts } from "../../slaughter-house/services/handle-fetch-slaughter-products";
import MonthlyDataCalendar from "../../../components/date-picker/MonthlyDataCalendar";
import PersianDate from "persian-date";
import axios from "axios";
import { LabelField } from "../../../components/label-field/LabelField";
import { SPACING } from "../../../data/spacing";
import { checkPathStartsWith } from "../../../utils/checkPathStartsWith";
export const StewardAllocationToGuild = ({
item,
key,
sellerType,
fetchData,
buyerType,
allocationType,
sellType,
updateTable,
fetchApiData,
editData,
coldHouseKey,
coldHouseItemKey,
killHouseAllocation,
priceInfo,
}) => {
const dispatch = useDispatch();
const [productData, setProductData] = useState([]);
const [guildsData, setGuildsData] = useState([]);
const [selectedInventory, setSelectedInventory] = useState("governmental");
const [approvedStatus, setApprovedStatus] = useState("true");
const [productKey, setProductKey] = useState(null);
const [openNotif] = useContext(AppContext);
const [profileImages, setProfileImages] = useState(
editData?.image ? [{ data_url: editData.image }] : []
);
const [value, setValue] = useState("own");
const [imageUploadLimit, setImageUploadLimit] = useState(1);
const [imageChanged, setImageChanged] = useState(false);
const [changeMobile, setChangeMobile] = useState(false);
const [selectedCalendarDate, setSelectedCalendarDate] = useState(null);
const [calendarDayData, setCalendarDayData] = useState({});
const [productionDate, setProductionDate] = useState(null);
const [selectedDateAmount, setSelectedDateAmount] = useState(null);
const [calendarRawData, setCalendarRawData] = useState({
governmental: [],
free: [],
});
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleChange = (event) => {
setValue(event.target.value);
setBuyerData({
key: "",
item: "",
buyerType: "",
allocationType: "",
});
};
useEffect(() => {
if (priceInfo?.active === false) {
setApprovedStatus("false");
}
}, [priceInfo?.active]);
useEffect(() => {
if (approvedStatus === "true" && priceInfo?.active) {
formik.setFieldValue("price", priceInfo?.killHousePrice);
}
}, [approvedStatus]);
const handleSellType = (event) => {
const newType = event.target.value;
setSelectedInventory(newType);
};
const handleApprovedPrice = (event) => {
const newType = event.target.value;
setApprovedStatus(newType);
};
const handleDateSelect = (dateInfo) => {
if (dateInfo && dateInfo.formattedDate) {
setSelectedCalendarDate(dateInfo.formattedDate);
const data = calendarDayData[dateInfo.formattedDate];
if (data && data.originalDay) {
setProductionDate(data.originalDay);
}
if (data && (data.amount !== undefined || data.value1 !== undefined)) {
const rawAmount = data.amount !== undefined ? data.amount : data.value1;
const normalizedAmount =
typeof rawAmount === "string"
? Number(rawAmount.replace(/,/g, ""))
: Number(rawAmount);
setSelectedDateAmount(
Number.isFinite(normalizedAmount) ? normalizedAmount : null
);
} else {
setSelectedDateAmount(null);
}
}
};
const transformCalendarData = useCallback((dataArray) => {
if (!Array.isArray(dataArray)) return {};
const transformedData = {};
dataArray.forEach((item) => {
if (item.day && item.amount !== undefined) {
const persianDate = new PersianDate(new Date(item.day));
const persianDateStr = persianDate.format("YYYY/MM/DD");
const rawAmount = item.amount;
const normalizedAmount =
typeof rawAmount === "string"
? Number(rawAmount.replace(/,/g, ""))
: Number(rawAmount);
transformedData[persianDateStr] = {
value1: normalizedAmount,
originalDay: item.day,
active: item.active === true, // Store active status
};
}
});
return transformedData;
}, []);
const updateCalendarData = useCallback((dataArray) => {
const transformed = transformCalendarData(dataArray);
setCalendarDayData(transformed);
}, []);
const fetchCalendarData = useCallback(
async (dateParam = selectedDate1) => {
try {
const response = await axios.get("/steward-remain-weight/", {
params: {
date: dateParam,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key
: "",
},
});
if (response.data) {
setCalendarRawData({
governmental: response.data.governmental || [],
free: response.data.free || [],
});
const dataToShow =
selectedInventory === "governmental"
? response.data.governmental
: response.data.free;
updateCalendarData(dataToShow || []);
}
} catch (error) {
console.error("Error fetching calendar data:", error);
}
},
[selectedInventory, updateCalendarData, selectedDate1, selectedSubUser]
);
const [buyerData, setBuyerData] = useState({
key,
item,
buyerType,
allocationType,
});
useEffect(() => {
if (getRoleFromUrl() === "Steward") {
setValue("free");
}
}, []);
useEffect(() => {
fetchCalendarData(selectedDate1);
}, [selectedDate1]);
useEffect(() => {
if (
calendarRawData.governmental.length > 0 ||
calendarRawData.free.length > 0
) {
const dataToShow =
selectedInventory === "governmental"
? calendarRawData.governmental
: calendarRawData.free;
updateCalendarData(dataToShow);
setSelectedCalendarDate(null);
setProductionDate(null);
setSelectedDateAmount(null);
}
}, [selectedInventory, calendarRawData]);
useEffect(() => {
dispatch(
provincePolicyGetUploadImageService({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
if (r.payload?.data) {
setImageUploadLimit(r.payload.data.killHouseAllocation);
}
});
if (!editData) {
dispatch(
slaughterGetProductsService({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setProductData(r.payload.data);
});
if (!item) {
dispatch(
slaughterGetGuildsForAllocateService({
free: value === "free" ? true : false,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key
: "",
})
).then((r) => {
setGuildsData(r.payload.data);
});
}
}
}, [value, selectedSubUser?.key]);
const validationSchema = Yup.object({
mobile: Yup.string().when([], {
is: () => !editData,
then: (schema) =>
schema
.required("شماره موبایل الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(
/^09\d{9}$/,
"شماره موبایل باید با 09 شروع شود و 11 رقم باشد"
),
otherwise: (schema) => schema.notRequired(),
}),
weight: Yup.number()
.required("این فیلد اجباری است!")
.integer("عدد باید صحیح باشد!")
.min(1, "یک مقدار مثبت وارد کنید!")
.test(
"max-production-date-amount",
`وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${
selectedDateAmount?.toLocaleString() || 0
} کیلوگرم) باشد!`,
function (value) {
if (!selectedDateAmount || selectedDateAmount === null) return true;
return (
value <= selectedDateAmount + (editData?.realWeightOfCarcasses || 0)
);
}
),
price: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!"),
wholePrice: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!"),
...(killHouseAllocation && {
image: Yup.string().when([], {
is: () => (!editData || imageChanged) && imageUploadLimit > 0,
then: Yup.string().required("عکس الزامی است"),
otherwise: Yup.string().notRequired(),
}),
}),
});
const factorPaymentHandler = (imageList) => {
if (imageList[0]) {
formik.setFieldValue("image", fixBase64(imageList[0]?.data_url));
setImageChanged(true);
} else {
formik.setFieldValue("image", "");
setImageChanged(true);
}
setProfileImages(imageList);
};
const formik = useFormik({
initialValues: {
mobile: "",
weight: editData?.realWeightOfCarcasses || "",
wholePrice: editData?.totalAmount || "",
price: editData?.amount || "",
image: editData?.image || "",
},
validationSchema,
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
formik.validateForm();
}, [selectedDateAmount]);
useEffect(() => {
if (formik.values.weight && formik.values.price) {
formik.setFieldValue(
"wholePrice",
formik.values.price * formik.values.weight
);
}
}, [formik.values.price, formik.values.weight]);
const successSubmit = () => {
dispatch(
fetchSlaughterBroadcastAndProducts({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
);
dispatch(
DRAWER({
right: false,
bottom: false,
left: false,
content: null,
})
);
fetchApiData && fetchApiData(1);
updateTable && updateTable();
fetchData && fetchData(1);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
};
const [dateRangeError, setDateRangeError] = useState(null);
return (
<Grid
container
xs={12}
direction="column"
justifyContent="center"
alignItems="flex-start"
gap={1.8}
>
{!editData && (
<DatePicker
label="تاریخ ثبت توزیع"
id="date"
renderInput={(params) => (
<TextField
fullWidth
{...params}
error={Boolean(dateRangeError) || params.error}
helperText={dateRangeError || params.helperText}
/>
)}
shouldDisableDate={(date) => {
const d = moment(date);
const today = moment();
const yesterday = moment().subtract(1, "day");
return !(d.isSame(today, "day") || d.isSame(yesterday, "day"));
}}
value={selectedDate1}
onChange={(e) => {
if (!e) {
setDateRangeError(null);
return;
}
const d = moment(e);
const today = moment();
const yesterday = moment().subtract(1, "day");
const isAllowed =
d.isSame(today, "day") || d.isSame(yesterday, "day");
if (!isAllowed) {
setDateRangeError(
"تنها امکان انتخاب «امروز» یا «دیروز» وجود دارد."
);
return;
}
setDateRangeError(null);
const formatted = moment(e).format("YYYY-MM-DD");
setSelectedDate1(formatted);
fetchCalendarData(formatted);
}}
/>
)}
{!editData && !coldHouseKey && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
productData
? productData.map((i) => {
return {
data: i,
label: `${i.name}`,
};
})
: []
}
onChange={(event, value) => {
setProductKey(value.data);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
)}
{!editData && (
<LabelField label="خریداران">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="own"
control={<Radio />}
label="اختصاصی"
/>
<FormControlLabel value="free" control={<Radio />} label="آزاد" />
</RadioGroup>
</FormControl>
</LabelField>
)}
{!item && !editData && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
guildsData
? guildsData.map((i) => {
return {
data: i,
label: `${i?.guildsName} ${i?.user?.fullname} (${i?.user?.mobile})`,
};
})
: []
}
onChange={(event, value) => {
setBuyerData({
item: value?.data,
key: value?.data?.key,
allocationType: "steward_guild",
buyerType: "Guild",
});
formik.setFieldValue("mobile", value?.data?.user?.mobile);
formik.setFieldTouched("mobile", true, false);
formik.validateField("mobile");
const reg = new RegExp(/^09\d{9}$/);
if (!reg.test(value?.data?.user?.mobile)) {
setChangeMobile(true);
}
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب صنف" />
)}
/>
</Grid>
)}
{!item && !editData && (
<Grid
container
xs={12}
alignItems="center"
justifyContent="center"
p={1}
gap={SPACING.TINY}
sx={{
border: 2,
borderColor: "#e6e6e6",
borderRadius: 2,
}}
>
<Typography variant="caption" color="error">
<Checkbox
sx={{ ml: -1.25 }}
checked={changeMobile}
onChange={() => setChangeMobile(!changeMobile)}
/>
از این قسمت میتوانید تلفن صنف را ویرایش کنید.
</Typography>
{buyerData?.key && changeMobile && (
<TextField
fullWidth
id="mobile"
value={formik.values.mobile}
error={
formik.touched.mobile ? Boolean(formik.errors.mobile) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.mobile && Boolean(formik.errors.mobile)
? formik.errors.mobile
: null
}
label="موبایل"
autoComplete="current-password"
variant="outlined"
/>
)}
</Grid>
)}
{!item && !editData && priceInfo?.active !== false && (
<LabelField label="نوع فروش">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={approvedStatus}
onChange={handleApprovedPrice}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value={true}
control={<Radio />}
label="قیمت دولتی"
/>
<FormControlLabel
value={false}
control={<Radio />}
label="قیمت آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField>
)}
{!item && !editData && (
<LabelField label="نوع انبار">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={selectedInventory}
onChange={handleSellType}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="governmental"
control={<Radio />}
label="دولتی"
/>
<FormControlLabel value="free" control={<Radio />} label="آزاد" />
</RadioGroup>
</FormControl>
</LabelField>
)}
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={SPACING.TINY}
sx={{ width: "100%" }}
direction="column"
>
<MonthlyDataCalendar
onDateSelect={handleDateSelect}
dayData={calendarDayData}
selectedDate={selectedCalendarDate}
maxGregorianDate={selectedDate1}
label={`تاریخ تولید گوشت ${
selectedDateAmount !== null
? `(موجودی: ${selectedDateAmount?.toLocaleString()} کیلوگرم)`
: ""
}`}
/>
{productionDate &&
selectedDate1 &&
moment(productionDate).isAfter(moment(selectedDate1), "day") && (
<Typography
sx={{
color: "#d32f2f",
fontSize: "0.75rem",
marginTop: "4px",
marginRight: "14px",
textAlign: "right",
}}
>
تاریخ تولید نمیتواند بعد از تاریخ انتخابی باشد
</Typography>
)}
</Grid>
<NumberInput
allowLeadingZeros
thousandSeparator=","
decimalScale={0}
allowNegative={false}
fullWidth
id="weight"
label="وزن لاشه"
variant="outlined"
value={formik.values.weight}
error={
!selectedDateAmount && !productionDate
? true
: formik.touched.weight
? Boolean(formik.errors.weight)
: selectedDateAmount && formik.values.weight > selectedDateAmount
}
onChange={(e) => {
const value = e.target.value;
if (value === "" || value === null || value === undefined) {
formik.setFieldValue("weight", "");
return;
}
const intValue = Math.floor(Number(value));
if (intValue > 0) {
formik.setFieldValue("weight", intValue);
} else if (intValue === 0) {
formik.setFieldValue("weight", "");
}
}}
onBlur={formik.handleBlur}
helperText={
!selectedDateAmount && !productionDate
? "لطفاً ابتدا تاریخ تولید را انتخاب کنید!"
: formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
disabled={!selectedDateAmount && !productionDate}
sx={{
"& .MuiFormHelperText-root": {
color:
selectedDateAmount && formik.values.weight > selectedDateAmount
? "error.main"
: undefined,
},
}}
/>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="price"
label="قیمت هر کیلوگرم"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.price}
error={formik.touched.price ? Boolean(formik.errors.price) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.price && Boolean(formik.errors.price)
? formik.errors.price
: null
}
/>
<NumberInput
disabled
allowLeadingZeros
thousandSeparator=","
fullWidth
id="wholePrice"
label="هزینه کل"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.wholePrice}
error={
formik.touched.wholePrice ? Boolean(formik.errors.wholePrice) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.wholePrice && Boolean(formik.errors.wholePrice)
? formik.errors.wholePrice
: null
}
/>
{(killHouseAllocation || (editData && editData.image)) && (
<Grid container xs={12} justifyContent="center" alignItems="center">
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"بارگزاری سند"}
/>
{formik.touched.image && Boolean(formik.errors.image) && (
<Typography color="error">ثبت تصویر الزامی است</Typography>
)}
</Grid>
)}
<Grid container xs={12} spacing={SPACING.SMALL}>
<Grid xs={6}>
<Button
variant="contained"
fullWidth
disabled={
editData
? !formik.isValid
: !formik.isValid ||
(coldHouseKey ? false : !productKey) ||
!buyerData?.item?.key ||
!productionDate ||
(productionDate &&
selectedDate1 &&
moment(selectedDate1).isBefore(
moment(productionDate),
"day"
))
}
onClick={() => {
let req = {};
if (coldHouseItemKey) {
req = {
allocation_key: coldHouseItemKey,
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
amount: formik.values.price,
total_amount: formik.values.wholePrice,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
distribution_type: "web",
...(imageChanged && { image: formik.values.image }),
};
} else if (!editData) {
req = {
seller_type: sellerType,
buyer_type: "Guild",
guild_key: buyerData?.item?.key,
cold_house_key: coldHouseKey || null,
product_key: coldHouseKey ? null : productKey.key,
type: "manual",
allocation_type: coldHouseKey ? "ColdHouse" : "steward_guild",
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
sell_type: sellType,
amount: formik.values.price,
total_amount: formik.values.wholePrice,
approved_price_status:
approvedStatus === "true" ? true : false,
quota: selectedInventory,
date: selectedDate1,
production_date: productionDate,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
distribution_type: "web",
...(buyerData?.item?.user?.mobile !== formik.values.mobile
? { interface_number: formik.values.mobile }
: {}),
...(profileImages.length > 0 && {
image: formik.values.image,
}),
};
req = Object.fromEntries(
Object.entries(req).filter(([, value]) => value !== null)
);
} else {
req = {
allocation_key: editData?.key,
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
amount: formik.values.price,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
total_amount: formik.values.wholePrice,
distribution_type: "web",
...(imageChanged && { image: formik.values.image }),
};
}
if (!editData) {
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
} else {
dispatch(slaughterEditAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
}
}}
>
{editData ? "ویرایش" : "ثبت"}
</Button>
</Grid>
<Grid xs={6}>
<Button
fullWidth
variant="outlined"
color="primary"
onClick={() => {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
}}
>
انصراف
</Button>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,609 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import InfoIcon from "@mui/icons-material/Info";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import SearchIcon from "@mui/icons-material/Search";
import axios from "axios";
import {
Box,
Button,
Card,
CardContent,
IconButton,
Typography,
} from "@mui/material";
import moment from "moment";
import AddIcon from "@mui/icons-material/Add";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import DeleteIcon from "@mui/icons-material/Delete";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../lib/redux/slices/appSlice";
import { slaughterGetProductsService } from "../../slaughter-house/services/slaughter-inventory-gets";
import {
slaughterDeleteDailyListService,
slaughterGetPriceService,
submitBatchAllocationsService,
} from "../../slaughter-house/services/slaughter-add-daily-list";
import { SlaughterAddDailyList } from "../../slaughter-house/components/slaughter-add-daily-list/SlaughterAddDailyList";
import { Grid } from "../../../components/grid/Grid";
import { NumberInput } from "../../../components/number-format-custom/NumberFormatCustom";
import ResponsiveTable from "../../../components/responsive-table/ResponsiveTable";
import { AppContext } from "../../../contexts/AppContext";
import { checkPathStartsWith } from "../../../utils/checkPathStartsWith";
export const StewardDailyList = () => {
const [productsTable, setProductsTable] = useState();
const [slaughterProducts, setSlaughterProducts] = useState();
const [prices, setPrices] = useState([]);
const [rendered, setrendered] = useState(false);
const [weights, setWeights] = useState([]);
const [data, setData] = useState([]);
const [tableData, setTableData] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [filteredData, setFilteredData] = useState([]);
const [openNotif] = useContext(AppContext);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const priceRefs = useRef([]);
const weightRefs = useRef([]);
const dispatch = useDispatch();
const [priceState, setPriceState] = useState({
active: false,
killHousePrice: 0,
stewardPrice: 0,
guildPrice: 0,
});
const getPriceByRole = () => {
const role = getRoleFromUrl();
if (role === "KillHouse") return priceState.killHousePrice;
if (role === "Steward") return priceState.stewardPrice;
if (role === "Guilds") return priceState.guildPrice;
return 0;
};
useEffect(() => {
priceRefs.current = priceRefs.current.slice(0, data?.length || 0);
weightRefs.current = weightRefs.current.slice(0, data?.length || 0);
}, [data]);
useEffect(() => {
if (searchTerm) {
const filtered = tableData.filter((item) =>
item.some((cell) =>
String(cell).toLowerCase().includes(searchTerm.toLowerCase())
)
);
setFilteredData(filtered);
} else {
setFilteredData(tableData);
}
}, [searchTerm, tableData]);
const handleKeyDown = (e, index, fieldType) => {
if (e.key === "Enter") {
e.preventDefault();
if (fieldType === "price") {
const updatedPrices = [...prices];
updatedPrices[index] = Number(e.target.value.replace(/,/g, ""));
setPrices(updatedPrices);
if (weightRefs.current[index]) {
weightRefs.current[index].focus();
}
} else if (fieldType === "weight") {
const updatedWeights = [...weights];
updatedWeights[index] = Number(e.target.value.replace(/,/g, ""));
setWeights(updatedWeights);
if (priceState?.active) {
let nextIndex = index + 1;
while (nextIndex < data.length) {
if (weightRefs.current[nextIndex]) {
weightRefs.current[nextIndex].focus();
break;
}
nextIndex++;
}
if (nextIndex >= data.length && weightRefs.current[0]) {
weightRefs.current[0]?.focus();
}
} else {
let nextIndex = index + 1;
while (nextIndex < data.length) {
if (priceRefs.current[nextIndex]) {
priceRefs.current[nextIndex].focus();
break;
}
nextIndex++;
}
if (nextIndex >= data.length && priceRefs.current[0]) {
priceRefs.current[0]?.focus();
}
}
}
}
};
const fetchPriceStatus = async () => {
dispatch(
slaughterGetPriceService({
role: getRoleFromUrl(),
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setPriceState(r.payload.data);
});
};
const fetchApiData = async () => {
dispatch(LOADING_START());
try {
const response = await axios.get(
`commonly-used/?search=filter&value=&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=1&page_size=10000`
);
setrendered(true);
setData(response.data.results || []);
} catch (e) {
console.error(e);
} finally {
dispatch(LOADING_END());
}
};
useEffect(() => {
const d = data?.map((item, i) => {
const getPrice =
parseInt(priceState?.active ? getPriceByRole() : prices[i]) *
parseInt(weights[i]);
return [
i + 1,
item?.guild?.steward ? "مباشر" : "صنف",
`${item?.guild?.guildsName}/${item?.guild?.user?.fullname}/${item?.guild?.user?.city}/${item?.guild?.user?.mobile}`,
item?.exclusive ? "اختصاصی" : "آزاد",
<NumberInput
allowLeadingZeros
thousandSeparator=","
key={`price-${i}`}
size="small"
label="قیمت"
disabled={priceState?.active}
value={priceState?.active ? getPriceByRole() : prices[i] || ""}
onKeyDown={(e) => handleKeyDown(e, i, "price")}
inputRef={(el) => (priceRefs.current[i] = el)}
variant="outlined"
style={{ width: 100 }}
/>,
<NumberInput
allowLeadingZeros
thousandSeparator=","
key={`weight-${i}`}
size="small"
label="وزن"
value={weights[i] || ""}
onKeyDown={(e) => {
handleKeyDown(e, i, "weight");
}}
inputRef={(el) => (weightRefs.current[i] = el)}
variant="outlined"
style={{ width: 100 }}
/>,
isNaN(getPrice) ? "وارد نشده! " : getPrice?.toLocaleString() + " ریال",
<IconButton key={i} color="error">
<DeleteIcon onClick={() => handleDeleteItem(item.key)} />
</IconButton>,
];
});
setTableData(d);
setFilteredData(d);
}, [data, prices, weights, priceState]);
useEffect(() => {
fetchApiData();
fetchPriceStatus();
dispatch(slaughterGetProductsService()).then((r) => {
setSlaughterProducts(r.payload.data);
});
}, [selectedSubUser?.key]);
useEffect(() => {
const d = slaughterProducts?.map((item) => {
return [item?.name, item?.totalRemainWeight?.toLocaleString()];
});
setProductsTable(d);
}, [slaughterProducts]);
const handleDeleteItem = (key) => {
dispatch(slaughterDeleteDailyListService(key)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
fetchApiData(1);
}
});
};
const handleSendAllocate = () => {
const filterdData = data?.map((item, i) => {
const price = priceState?.active ? getPriceByRole() : prices[i];
let d = {
seller_type: "Steward",
buyer_type: item?.guild?.steward ? "Steward" : "Guild",
guild_key: !item?.guild?.steward ? item?.guild?.key : null,
steward_key: item?.guild?.steward ? item?.guild?.key : null,
product_key: slaughterProducts[0]?.key,
type: "manual",
allocation_type: item?.steward ? "steward_steward" : "steward_guild",
number_of_carcasses: 0,
weight_of_carcasses: weights[i] || null,
sell_type: "free",
amount: price || null,
total_amount: price * weights[i],
approved_price_status: priceState?.active,
date: moment(new Date()).format("YYYY-MM-DD"),
};
d = Object.fromEntries(
Object.entries(d).filter(([_, value]) => value !== null)
);
return d;
});
const finalData = filterdData.filter(
(option) =>
option.total_amount > 1 && option?.amount && option?.weight_of_carcasses
);
dispatch(submitBatchAllocationsService(finalData)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
setPrices([]);
setWeights([]);
fetchApiData(1);
fetchPriceStatus();
dispatch(slaughterGetProductsService()).then((r) => {
setSlaughterProducts(r.payload.data);
});
}
});
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Grid container xs={12} justifyContent="center" alignItems="center">
<ResponsiveTable
noPagination
title={"موجودی انبار"}
columns={["محصول", "مانده انبار (کیلوگرم)"]}
data={productsTable}
customColors={[{ name: "محصول", color: "red" }]}
/>
</Grid>
<Grid
xs={12}
container
spacing={2}
mt={2}
alignItems="center"
justifyContent="space-between"
>
<Grid item xs={12} md="auto">
<Button
variant="contained"
startIcon={<AddIcon />}
sx={{ borderRadius: 3, px: 3 }}
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <SlaughterAddDailyList updateTable={fetchApiData} />,
title: "افزودن",
})
);
}}
>
افزودن مباشر/صنف
</Button>
</Grid>
<Grid item xs={12} md>
<Card variant="outlined" sx={{ borderRadius: 3, boxShadow: 1 }}>
<CardContent>
<Grid
container
spacing={2}
alignItems="center"
justifyContent="space-between"
>
<Grid item>
<Typography variant="subtitle2">
مجموع وزن وارد شده
</Typography>
<Typography variant="body1" color="text.secondary">
{weights?.length
? weights.reduce((a, b) => a + b, 0).toLocaleString()
: "۰"}
</Typography>
</Grid>
<Grid item>
<Typography variant="subtitle2">وزن باقیمانده</Typography>
<Typography
variant="body1"
color={
weights?.length && slaughterProducts
? weights?.reduce((a, b) => a + b, 0) >
slaughterProducts[0]?.totalRemainWeight
? "error"
: "text.secondary"
: "text.secondary"
}
>
{slaughterProducts?.[0]?.totalRemainWeight !== undefined
? weights?.length
? (
slaughterProducts[0]?.totalRemainWeight -
weights.reduce((a, b) => a + b, 0)
).toLocaleString()
: slaughterProducts[0]?.totalRemainWeight.toLocaleString()
: "۰"}
</Typography>
</Grid>
<Grid item>
<Button
variant="contained"
color="success"
startIcon={<CheckCircleIcon />}
sx={{ borderRadius: 3, px: 3 }}
onClick={handleSendAllocate}
disabled={
weights.length
? weights.reduce((a, b) => a + b, 0) >
slaughterProducts[0]?.totalRemainWeight
: true
}
>
ثبت
</Button>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
<Grid container xs={12} mt={2} gap={1} sx={{ userSelect: "none" }}>
<InfoIcon color="error" />
<Typography variant="body1" color="error">
پس از وارد کردن هر مقدار، کلید Enter را فشار دهید!
</Typography>
</Grid>
<Grid container xs={12} mt={2} gap={1} sx={{ userSelect: "none" }}>
<InfoIcon color="primary" />
<Typography variant="body1" color="primary">
صرفا تخصیصاتی که هر دو مقدار قیمت و وزن آنها را وارد کنید ثبت خواهند
شد.
</Typography>
</Grid>
<Grid container xs={12} mt={2}>
<TextField
fullWidth
variant="outlined"
placeholder="جستجو..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
sx={{ mb: 2 }}
/>
</Grid>
{filteredData?.length ? (
<Grid container xs={12} gap={1} mt={2} mb={2}>
{filteredData?.map((item, i) => (
<Grid
container
xs={12}
key={i}
sx={{
p: 2,
pl: 5,
borderRadius: 2,
backgroundColor: i % 2 === 0 ? "#fef6f0" : "#ffffff",
boxShadow: "0 2px 6px rgba(0,0,0,0.05)",
position: "relative",
flexDirection: "row",
gap: 2,
alignItems: "center",
}}
>
<Box
sx={{
position: "absolute",
top: 12,
left: 12,
backgroundColor: "#fb8c00",
width: 28,
height: 28,
borderRadius: "50%",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "0.8rem",
fontWeight: 700,
boxShadow: "0 1px 4px rgba(0,0,0,0.2)",
}}
>
{item[0]}
</Box>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
ماهیت:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[1]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
خریدار:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[2]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
نوع فروش:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[3]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography variant="caption" color="text.secondary">
قیمت هرکیلو:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[4]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography variant="caption" color="text.secondary">
وزن لاشه:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[5]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
قیمت کل:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[6]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
{item[7]}
</Grid>
<Grid item>
{!priceState?.active &&
(!prices[i] || !weights[i]) &&
(prices[i] || weights[i]) && (
<Typography variant="caption" color="error">
لطفا همه موارد را وارد کنید و کلید Enter را بزنید
</Typography>
)}
</Grid>
</Grid>
))}
</Grid>
) : (
<Typography mt={4}>
{!rendered
? searchTerm
? "نتیجه‌ای یافت نشد"
: "در حال بارگزاری..."
: "موردی یافت نشد!"}
</Typography>
)}
</Grid>
);
};

View File

@@ -0,0 +1,21 @@
import { Grid } from "../../../components/grid/Grid";
import ResponsiveTable from "../../../components/responsive-table/ResponsiveTable";
export const StewardDispenserSaleOutDashboard = ({ dashboardData }) => {
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<ResponsiveTable
noPagination
title={"اطلاعات کلی"}
columns={["تعداد فروش", "خریدار", "وزن کل لاشه ها"]}
data={[
[
dashboardData?.numberOfBars?.toLocaleString(),
dashboardData?.numberOfBuyers?.toLocaleString(),
dashboardData?.barsWeight?.toLocaleString(),
],
]}
/>
</Grid>
);
};

View File

@@ -0,0 +1,230 @@
import React, { useContext } from "react";
import {
Grid,
TextField,
Button,
RadioGroup,
FormControlLabel,
Radio,
FormControl,
} from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { useDispatch } from "react-redux";
import { guildAllocatedStockOperationService } from "../services/guild-allocated-stock-operation";
import { AppContext } from "../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../lib/redux/slices/appSlice";
export const StewardNorEnterdBarChangeState = ({
item,
handleUpdate,
updateTable,
}) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
decision: "approve",
weight: item?.realWeightOfCarcasses,
volume: item?.realNumberOfCarcasses,
weightLoss: item?.weightLossOfCarcasses,
regCode: "",
regState: "with_code",
},
validationSchema: Yup.object({
decision: Yup.string().required("انتخاب گزینه الزامی است"),
regState: Yup.string(),
weight: Yup.string().when("decision", {
is: "approve",
then: Yup.string().required("وزن الزامی است"),
}),
volume: Yup.string().when("decision", {
is: "approve",
then: Yup.string().required("حجم الزامی است"),
}),
weightLoss: Yup.string().when("decision", {
is: "approve",
then: Yup.string().required("افت وزن الزامی است"),
}),
regCode: Yup.string()
.matches(/^\d{5}$/, "کد باید یک عدد پنج رقمی باشد")
.typeError("یک عدد پنج رقمی وارد کنید!")
.when(["regState", "decision"], {
is: (regState, decision) =>
regState === "with_code" && decision === "approve",
then: Yup.string().required("کد احراز الزامی است"),
}),
}),
onSubmit: (values) => {
const baseRequest = {
check_allocation: true,
allocation_key: item?.key,
};
const req =
values.decision === "reject"
? { ...baseRequest, state: "rejected" }
: {
...baseRequest,
state: "accepted",
...(values.regState === "with_code" && {
registration_code: parseInt(values.regCode),
}),
receiver_real_number_of_carcasses: parseInt(values.volume),
receiver_real_weight_of_carcasses: parseInt(values.weight),
weight_loss_of_carcasses: parseInt(values.weightLoss),
};
dispatch(guildAllocatedStockOperationService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
handleUpdate();
updateTable && updateTable();
}
});
},
});
const handleDecisionChange = (event) => {
formik.setFieldValue("decision", event.target.value);
};
return (
<Grid container spacing={2} justifyContent="center" alignItems="center">
<Grid item xs={12}>
<FormControl component="fieldset">
<RadioGroup
fullWidth
row
name="decision"
value={formik.values.decision}
onChange={handleDecisionChange}
>
<FormControlLabel
value="approve"
control={<Radio />}
label="تایید"
/>
<FormControlLabel value="reject" control={<Radio />} label="رد" />
</RadioGroup>
</FormControl>
{formik.touched.decision && formik.errors.decision && (
<div style={{ color: "red", fontSize: 12 }}>
{formik.errors.decision}
</div>
)}
</Grid>
{formik.values.decision === "approve" && (
<>
<Grid item xs={12}>
<TextField
fullWidth
name="weight"
label="وزن"
value={formik.values.weight}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.weight && Boolean(formik.errors.weight)}
helperText={formik.touched.weight && formik.errors.weight}
/>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
name="volume"
label="حجم"
value={formik.values.volume}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.volume && Boolean(formik.errors.volume)}
helperText={formik.touched.volume && formik.errors.volume}
/>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
name="weightLoss"
label="افت وزن (کیلوگرم)"
value={formik.values.weightLoss}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.weightLoss && Boolean(formik.errors.weightLoss)
}
helperText={formik.touched.weightLoss && formik.errors.weightLoss}
/>
</Grid>
<Grid item xs={12}>
<FormControl component="fieldset">
<RadioGroup
fullWidth
row
name="regState"
value={formik.values.regState}
onChange={(e) => {
formik.setFieldValue("regState", e.target.value);
}}
>
<FormControlLabel
value="with_code"
control={<Radio />}
label="با کد احراز"
/>
<FormControlLabel
value="without_code"
control={<Radio />}
label="بدون کد احراز"
/>
</RadioGroup>
</FormControl>
{formik.touched.regState && formik.errors.regState && (
<div style={{ color: "red", fontSize: 12 }}>
{formik.errors.regState}
</div>
)}
</Grid>
{formik.values.regState === "with_code" && (
<Grid item xs={12}>
<TextField
fullWidth
name="regCode"
label="کد احراز"
value={formik.values.regCode}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.regCode && Boolean(formik.errors.regCode)}
helperText={formik.touched.regCode && formik.errors.regCode}
/>
</Grid>
)}
</>
)}
<Grid item xs={12}>
<Button
onClick={formik.handleSubmit}
type="submit"
variant="contained"
color={formik.values.decision === "approve" ? "primary" : "error"}
fullWidth
>
ثبت
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,27 @@
import { Grid } from "../../../components/grid/Grid";
import ResponsiveTable from "../../../components/responsive-table/ResponsiveTable";
export const StewardSaleDispenserWithinDashboard = ({ dashboardData }) => {
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<ResponsiveTable
noPagination
title={"اطلاعات کلی"}
columns={[
"تعداد توزیع",
"کل وزن توزیع",
"وزن توزیع به مباشر",
"وزن توزیع به صنف",
]}
data={[
[
dashboardData?.numberOfAllocations?.toLocaleString(),
dashboardData?.totalWeight?.toLocaleString(),
dashboardData?.totalStewardWeight?.toLocaleString(),
dashboardData?.totalGuildWeight?.toLocaleString(),
],
]}
/>
</Grid>
);
};

View File

@@ -0,0 +1,316 @@
import { useContext, useEffect, useState } from "react";
import { AppContext } from "../../../contexts/AppContext";
import { useDispatch, useSelector } from "react-redux";
import ResponsiveTable from "../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../components/grid/Grid";
import moment from "moment";
import { Button, TextField } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { RiSearchLine } from "react-icons/ri";
import { SPACING } from "../../../data/spacing";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { StewardSegmentSubmitOperation } from "./StewardSegmentSubmitOperation";
import { stewardGetOutSellService } from "../services/steward-get-sell-out-service";
import { formatJustDate, formatTime } from "../../../utils/formatTime";
import { StewardSegmentOperation } from "./StewardSegmentOperation";
import { stawardGetSegmentDashboardService } from "../services/steward-get-dashboard-service";
import { checkPathStartsWith } from "../../../utils/checkPathStartsWith";
export const StewardSegmant = () => {
const [data, setData] = useState([]);
const [products, setProducts] = useState([]);
const [tableData, setTableData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [dashboardData, setDashboardData] = useState([]);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
// const userKey = useSelector((state) => state.userSlice.userProfile.key);
const getDashboardData = () => {
dispatch(
stawardGetSegmentDashboardService({
value: textValue,
date1: selectedDate1,
date2: selectedDate2,
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`app-segmentation/?search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
getDashboardData();
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage]);
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const updateTable = () => {
fetchApiData(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
fetchApiData(1);
dispatch(
stewardGetOutSellService({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setProducts(r.payload.data);
});
}, [selectedSubUser?.key]);
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.toGuild ? "قطعه بند" : "مباشر",
`${item?.buyer?.fullname}(${item?.buyer?.mobile})`,
item?.toGuild
? `${item?.toGuild?.user?.fullname}(${item?.toGuild?.user?.mobile})`
: "-",
item?.date ? formatTime(item?.date) : "-",
item?.productionDate ? formatJustDate(item?.productionDate) : "-",
item?.distributionType === "web"
? "سایت"
: item?.distributionType === "app"
? "موبایل"
: item?.distributionType === "pos"
? "پوز"
: item?.distributionType || "-",
item?.weight,
item?.quota === "governmental"
? "دولتی"
: item?.quota === "free"
? "آزاد"
: "-",
item?.saleType === "governmental"
? "دولتی"
: item?.saleType === "free"
? "آزاد"
: "-",
<StewardSegmentOperation
key={i}
item={item}
productKey={products?.[0]?.key}
updateTable={updateTable}
/>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`app-segmentation/?search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
getDashboardData();
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
return (
<Grid container direction="column" flexWrap="nowrap" mt={SPACING.SMALL}>
<Grid
xs={12}
container
alignItems="center"
gap={SPACING.SMALL}
justifyContent="flex-start"
>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"وزن کل (کیلوگرم)",
// "تخصیصات مباشر",
"وزن قطعه بندی",
// "تخصیصات قطعه بند",
"وزن تخصیصی به قطعه بندها",
]}
data={[
[
dashboardData?.totalWeight?.toLocaleString() || "0",
// dashboardData?.totalSelfCount?.toLocaleString() || "0",
dashboardData?.totalSelfWeight?.toLocaleString() || "0",
// dashboardData?.totalOtherCount?.toLocaleString() || "0",
dashboardData?.totalOtherWeight?.toLocaleString() || "0",
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<form>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت ",
width: {
xs: "100%",
sm: "360px",
},
content: (
<StewardSegmentSubmitOperation
updateTable={updateTable}
productKey={products?.[0]?.key}
/>
),
})
);
}}
>
ثبت قطعه بندی
</Button>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<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>
{/* <Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
const link = `${
axios.defaults.baseURL
}kill_house_free_bar_excel/?role=${getRoleFromUrl()}&key=${userKey}&date1=${selectedDate1}&date2=${selectedDate2}&type=carcass&search=filter&value=${textValue}&date_type=buy`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
</Tooltip> */}
</Grid>
</form>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"ماهیت",
"مباشر",
"تخصیص به قطعه بند",
"تاریخ ",
"تاریخ تولید گوشت",
"ثبت شده",
"وزن (کیلوگرم)",
"سهمیه",
"نوع فروش",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="قطعه بندی"
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,108 @@
import { useState } from "react";
import { useDispatch } from "react-redux";
import { stewardDeleteSegmentService } from "../services/steward-segment-delete-operation";
import {
IconButton,
Popover,
List,
ListItem,
ListItemButton,
ListItemIcon,
Typography,
} from "@mui/material";
import { DRAWER } from "../../../lib/redux/slices/appSlice";
import { StewardSegmentSubmitOperation } from "./StewardSegmentSubmitOperation";
import TuneIcon from "@mui/icons-material/Tune";
import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
import DeleteOutlineOutlinedIcon from "@mui/icons-material/DeleteOutlineOutlined";
export const StewardSegmentOperation = ({ item, updateTable, productKey }) => {
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const handleDelete = () => {
handleClose();
dispatch(stewardDeleteSegmentService(item.key)).then(() => {
updateTable();
});
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<List sx={{ p: 1 }}>
<ListItem disablePadding>
<ListItemButton
sx={{ color: "primary.main" }}
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش قطعه بندی",
content: (
<StewardSegmentSubmitOperation
item={item}
updateTable={updateTable}
editData={item}
productKey={productKey}
/>
),
})
);
}}
>
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
<EditOutlinedIcon fontSize="small" />
</ListItemIcon>
<Typography variant="body2">ویرایش</Typography>
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton sx={{ color: "error.main" }} onClick={handleDelete}>
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
<DeleteOutlineOutlinedIcon fontSize="small" />
</ListItemIcon>
<Typography variant="body2" color="inherit">
حذف
</Typography>
</ListItemButton>
</ListItem>
</List>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,654 @@
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
InputAdornment,
Radio,
RadioGroup,
TextField,
} from "@mui/material";
import { SPACING } from "../../../data/spacing";
import { Grid } from "../../../components/grid/Grid";
import { DRAWER } from "../../../lib/redux/slices/appSlice";
import { stewardSegmentSubmitService } from "../services/steward-segment-submit-service";
import { useDispatch, useSelector } from "react-redux";
import { useContext, useEffect, useState, useCallback } from "react";
import { AppContext } from "../../../contexts/AppContext";
import { useFormik } from "formik";
import { Yup } from "../../../lib/yup/yup";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { stewardEditSegmentService } from "../services/steward-segment-edit-operation";
import MonthlyDataCalendar from "../../../components/date-picker/MonthlyDataCalendar";
import PersianDate from "persian-date";
import axios from "axios";
import { LabelField } from "../../../components/label-field/LabelField";
import { checkPathStartsWith } from "../../../utils/checkPathStartsWith";
import {
slaughterGetGuildsForAllocateService,
slaughterGetStewardsForAllocateService,
} from "../../slaughter-house/services/slaughter-get-guilds-for-allocate";
import { Typography } from "@mui/material";
const getValidationSchema = (selectedDateAmount) =>
Yup.object().shape({
weight: Yup.number()
.required("وزن لاشه الزامی است")
.min(0.01, "وزن باید بیشتر از 0 باشد")
.test(
"max-production-date-amount",
`وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${
selectedDateAmount?.toLocaleString() || 0
} کیلوگرم) باشد!`,
function (value) {
if (!selectedDateAmount || selectedDateAmount === null) return true;
return value <= selectedDateAmount;
}
),
product_key: Yup.string().when("segmentType", {
is: "own",
then: Yup.string().required("انتخاب کلید الزامی است"),
}),
});
export const StewardSegmentSubmitOperation = ({
updateTable,
productKey,
editData,
item,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [segmentType, setSegmentType] = useState("own");
const [selectedInventory] = useState("free");
const [approvedStatus, setApprovedStatus] = useState("governmental");
const [buyerCategory, setBuyerCategory] = useState(""); // "stewards" or "guilds"
const [guildsData, setGuildsData] = useState([]);
const [stewardsData, setStewardsData] = useState([]);
const [buyerData, setBuyerData] = useState({
key: "",
item: "",
buyerType: "",
allocationType: "",
});
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [selectedCalendarDate, setSelectedCalendarDate] = useState(null);
const [calendarDayData, setCalendarDayData] = useState({});
const [productionDate, setProductionDate] = useState(null);
const [selectedDateAmount, setSelectedDateAmount] = useState(null);
const [calendarRawData, setCalendarRawData] = useState({
governmental: [],
free: [],
});
const formik = useFormik({
initialValues: {
product_key: productKey || editData?.productkey || "",
weight: editData?.weight || "",
segmentType: editData ? (editData?.guildkey ? "free" : "own") : "own",
},
validationSchema: getValidationSchema(selectedDateAmount),
onSubmit: (values) => {
if (editData) {
const req = {
weight: values.weight,
key: item?.key,
};
dispatch(stewardEditSegmentService(req)).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 }));
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
let req;
if (segmentType === "own") {
req = {
product_key: values?.product_key,
weight: values.weight,
sale_type: selectedInventory,
quota: approvedStatus,
production_date: productionDate,
distribution_type: "web",
};
} else {
if (!buyerData?.key) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا مباشر یا صنف را انتخاب کنید",
severity: "error",
});
return;
}
req = {
guild_key: buyerData?.buyerType === "Guild" ? buyerData?.key : null,
steward_key:
buyerData?.buyerType === "Steward" ? buyerData?.key : null,
weight: values.weight,
product_key: productKey || "",
sale_type: selectedInventory,
quota: approvedStatus,
production_date: productionDate,
distribution_type: "web",
};
req = Object.fromEntries(
Object.entries(req).filter(([, val]) => val !== null)
);
}
dispatch(stewardSegmentSubmitService(req)).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 }));
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
},
});
const handleSegmentTypeChange = (event) => {
const newType = event.target.value;
setSegmentType(newType);
formik.setFieldValue("segmentType", newType);
};
// const handleSellType = (event) => {
// const newType = event.target.value;
// setSelectedInventory(newType);
// };
const handleApprovedPrice = (event) => {
const newType = event.target.value;
setApprovedStatus(newType);
};
const handleBuyerCategoryChange = (event) => {
const newCategory = event.target.value;
setBuyerCategory(newCategory);
setBuyerData({
key: "",
item: "",
buyerType: "",
allocationType: "",
});
setGuildsData([]);
setStewardsData([]);
};
// Calendar functions
const handleDateSelect = (dateInfo) => {
if (dateInfo && dateInfo.formattedDate) {
setSelectedCalendarDate(dateInfo.formattedDate);
// Get the data for the selected date
const data = calendarDayData[dateInfo.formattedDate];
if (data && data.originalDay) {
setProductionDate(data.originalDay);
}
// Get the amount for the selected date
if (data && data.value1 !== undefined) {
setSelectedDateAmount(data.value1);
} else {
setSelectedDateAmount(null);
}
}
};
const transformCalendarData = useCallback((dataArray) => {
if (!Array.isArray(dataArray)) return {};
const transformedData = {};
dataArray.forEach((item) => {
if (item.day && item.amount !== undefined) {
const persianDate = new PersianDate(new Date(item.day));
const persianDateStr = persianDate.format("YYYY/MM/DD");
transformedData[persianDateStr] = {
value1: item.amount,
originalDay: item.day,
active: item.active === true,
};
}
});
return transformedData;
}, []);
const updateCalendarData = useCallback(
(dataArray) => {
const transformed = transformCalendarData(dataArray);
setCalendarDayData(transformed);
},
[transformCalendarData]
);
// Fetch calendar data from API
const fetchCalendarData = useCallback(async () => {
try {
const currentRole = getRoleFromUrl();
console.log(getRoleFromUrl());
let remainWeightEndpoint = "kill-house-remain-weight";
if (currentRole === "Steward") {
remainWeightEndpoint = "steward-remain-weight";
} else if (currentRole === "Guilds") {
remainWeightEndpoint = "guild-remain-weight";
}
const response = await axios.get(`${remainWeightEndpoint}/`, {
params: {
role_key:
checkPathStartsWith("slaughter") ||
checkPathStartsWith("steward") ||
checkPathStartsWith("senf")
? selectedSubUser?.key || ""
: "",
},
});
if (response.data) {
setCalendarRawData({
governmental: response.data.governmental || [],
free: response.data.free || [],
});
const dataToShow =
approvedStatus === "governmental"
? response.data.governmental
: response.data.free;
updateCalendarData(dataToShow);
}
} catch (error) {
console.error("Error fetching calendar data:", error);
}
}, [approvedStatus, updateCalendarData, selectedSubUser]);
useEffect(() => {
fetchCalendarData();
}, []);
useEffect(() => {
if (!editData && buyerCategory) {
if (buyerCategory === "guilds") {
dispatch(
slaughterGetGuildsForAllocateService({
free: true,
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setGuildsData(r.payload.data || []);
});
} else if (buyerCategory === "stewards") {
dispatch(
slaughterGetStewardsForAllocateService({
free: true,
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setStewardsData(r.payload.data || []);
});
}
}
}, [buyerCategory, selectedSubUser?.key, editData]);
useEffect(() => {
if (
calendarRawData.governmental.length > 0 ||
calendarRawData.free.length > 0
) {
const dataToShow =
approvedStatus === "governmental"
? calendarRawData.governmental
: calendarRawData.free;
updateCalendarData(dataToShow);
setSelectedCalendarDate(null);
setProductionDate(null);
setSelectedDateAmount(null);
}
}, [approvedStatus, calendarRawData, updateCalendarData]);
useEffect(() => {
formik.validateForm();
}, [selectedDateAmount]);
// Set segmentType to "own" for Guilds role
useEffect(() => {
const currentRole = getRoleFromUrl();
if (currentRole === "Guilds" && !editData) {
setSegmentType("own");
formik.setFieldValue("segmentType", "own");
}
}, [editData]);
return (
<Grid container direction="column" justifyContent="center" gap={2}>
<Grid container direction="column" justifyContent="center" gap={2} pt={2}>
<form
onSubmit={formik.handleSubmit}
style={{
display: "flex",
flexDirection: "column",
gap: SPACING.LARGE + 4,
}}
>
{!editData && getRoleFromUrl() !== "Guilds" && (
<LabelField label="قطعه بندی (کاربر)">
<FormControl>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={segmentType}
onChange={handleSegmentTypeChange}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="own"
control={<Radio />}
label="قطعه بندی (کاربر)"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="تخصیص به قطعه بند"
/>
</RadioGroup>
</FormControl>
</LabelField>
)}
{!editData && segmentType === "free" && (
<>
<LabelField label="خریداران">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="buyer-category-radio-group"
name="buyerCategory"
value={buyerCategory}
onChange={handleBuyerCategoryChange}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="stewards"
control={<Radio />}
label="مباشرین"
/>
<FormControlLabel
value="guilds"
control={<Radio />}
label="اصناف"
/>
</RadioGroup>
</FormControl>
</LabelField>
{buyerCategory && (
<Grid xs={12} container>
{(() => {
const currentData =
buyerCategory === "guilds" ? guildsData : stewardsData;
const isEmpty = !currentData || currentData.length === 0;
if (isEmpty) {
return (
<Typography
variant="body2"
color="text.secondary"
sx={{
width: "100%",
textAlign: "center",
padding: 2,
fontStyle: "italic",
}}
>
{buyerCategory === "guilds"
? "هیچ صنفی یافت نشد"
: "هیچ مباشری یافت نشد"}
</Typography>
);
}
return (
<Autocomplete
fullWidth
disablePortal
id="buyer-selection"
options={
buyerCategory === "guilds"
? guildsData.map((i) => {
return {
data: i,
label: `${i?.guildsName} ${i?.user?.fullname} (${i?.user?.mobile})`,
};
})
: stewardsData.map((i) => {
return {
data: i,
label: `${i?.name || ""} - ${
i?.user?.fullname || ""
} (${i?.user?.mobile || ""})`,
};
})
}
onChange={(event, value) => {
if (buyerCategory === "guilds") {
setBuyerData({
item: value?.data,
key: value?.data?.key,
allocationType: value?.data?.steward
? "steward_steward"
: "steward_guild",
buyerType: value?.data?.steward
? "Steward"
: "Guild",
});
} else if (buyerCategory === "stewards") {
setBuyerData({
item: value?.data,
key: value?.data?.key,
allocationType: "steward_steward",
buyerType: "Steward",
});
}
}}
renderInput={(params) => (
<TextField
fullWidth
{...params}
label={
buyerCategory === "guilds"
? "انتخاب صنف"
: "انتخاب مباشر"
}
/>
)}
/>
);
})()}
</Grid>
)}
</>
)}
{/* {!editData && (
<LabelField label="نوع فروش">
<FormControl>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={selectedInventory}
onChange={handleSellType}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
disabled
value="governmental"
control={<Radio />}
label="قیمت دولتی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="قیمت آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField>
)} */}
{!editData && (
<LabelField label="نوع انبار">
<FormControl>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={approvedStatus}
onChange={handleApprovedPrice}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="governmental"
control={<Radio />}
label="دولتی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField>
)}
{!editData && (
<Grid
style={{ width: "100%" }}
container
xs={12}
lg={3}
justifyContent="center"
alignItems="center"
gap={1}
>
<MonthlyDataCalendar
onDateSelect={handleDateSelect}
dayData={calendarDayData}
selectedDate={selectedCalendarDate}
label={`تاریخ تولید گوشت ${
selectedDateAmount !== null
? `(موجودی: ${selectedDateAmount?.toLocaleString()} کیلوگرم)`
: ""
}`}
/>
</Grid>
)}
<TextField
id="weight"
name="weight"
label="وزن لاشه"
type="number"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
value={formik.values.weight}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
(formik.touched.weight && Boolean(formik.errors.weight)) ||
(selectedDateAmount && formik.values.weight > selectedDateAmount)
}
helperText={
selectedDateAmount && formik.values.weight > selectedDateAmount
? `وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${selectedDateAmount?.toLocaleString()} کیلوگرم) باشد!`
: formik.touched.weight && formik.errors.weight
}
fullWidth
/>
<Grid container spacing={2}>
<Grid xs={6}>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
disabled={
!editData &&
(!productionDate ||
(selectedDateAmount &&
formik.values.weight > selectedDateAmount) ||
(segmentType === "free" && !buyerData?.key))
}
>
{editData ? "ویرایش" : "ثبت"}
</Button>
</Grid>
<Grid xs={6}>
<Button
fullWidth
variant="outlined"
color="primary"
onClick={() => {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
}}
>
انصراف
</Button>
</Grid>
</Grid>
</form>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,141 @@
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import {
Button,
FormControl,
Grid,
InputLabel,
MenuItem,
Select,
Typography,
} from "@mui/material";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SPACING } from "../../../../data/spacing";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
export const AddAccessLevelModal = ({ device, onSuccess }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [selectedAccessLevel, setSelectedAccessLevel] = useState("");
const [submitting, setSubmitting] = useState(false);
const accessLevelOptions = ["Steward", "KillHouse", "Guilds"];
const handleCloseModal = () => {
dispatch(CLOSE_MODAL());
};
const handleSubmit = async () => {
if (!selectedAccessLevel) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg: "لطفاً یک سطح دسترسی را انتخاب کنید.",
});
return;
}
if (!device?.key && !device?.id) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg: "شناسه دستگاه یافت نشد. لطفاً دوباره تلاش کنید.",
});
return;
}
setSubmitting(true);
try {
const payload = {
pos_key: device?.key || device?.id,
name: selectedAccessLevel,
};
await axios.post("/pos-access-level/", payload);
openNotif({
vertical: "top",
horizontal: "center",
severity: "success",
msg: "سطح دسترسی با موفقیت افزوده شد.",
});
if (onSuccess) {
onSuccess();
}
handleCloseModal();
} catch (error) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg:
error?.response?.data?.result ||
error?.response?.data?.detail ||
"افزودن سطح دسترسی با خطا مواجه شد.",
});
} finally {
setSubmitting(false);
}
};
return (
<Grid
container
direction="column"
gap={SPACING.SMALL}
width="100%"
alignItems="stretch"
sx={{ minWidth: 300 }}
>
<Typography variant="body2">
دستگاه انتخاب شده:{" "}
{device?.serial ||
device?.pos_unique_id ||
device?.pos_id ||
device?.posId ||
"-"}
</Typography>
<FormControl fullWidth size="small">
<InputLabel id="access-level-label">سطح دسترسی</InputLabel>
<Select
labelId="access-level-label"
id="access-level-select"
value={selectedAccessLevel}
label="سطح دسترسی"
onChange={(e) => setSelectedAccessLevel(e.target.value)}
>
<MenuItem value="">
<em>انتخاب سطح دسترسی</em>
</MenuItem>
{accessLevelOptions.map((option) => (
<MenuItem key={option} value={option}>
{getFaUserRole(option)}
</MenuItem>
))}
</Select>
</FormControl>
<Grid container justifyContent="flex-end" gap={SPACING.SMALL} mt={2}>
<Button
variant="outlined"
color="primary"
onClick={handleCloseModal}
disabled={submitting}
>
انصراف
</Button>
<Button
variant="contained"
onClick={handleSubmit}
disabled={!selectedAccessLevel || submitting}
>
{submitting ? "در حال ثبت..." : "ثبت"}
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,354 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
Autocomplete,
Button,
CircularProgress,
FormControl,
FormControlLabel,
Grid,
MenuItem,
Radio,
RadioGroup,
Select,
TextField,
Typography,
} from "@mui/material";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { LabelField } from "../../../../components/label-field/LabelField";
import { SPACING } from "../../../../data/spacing";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const AssignSubUserModal = ({ device, onSuccess }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [options, setOptions] = useState([]);
const [selectedUser, setSelectedUser] = useState(null);
const [selectedAccessLevel, setSelectedAccessLevel] = useState("");
const [loading, setLoading] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [fetchError, setFetchError] = useState("");
const [userType, setUserType] = useState("delegate");
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
let isMounted = true;
const fetchUsers = async () => {
setLoading(true);
setFetchError("");
setSelectedUser(null);
setSelectedAccessLevel("");
try {
let response;
const role = getRoleFromUrl();
if (userType === "delegate") {
response = await axios.get(
`/get_representatives/?role=${role}${
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
} else {
response = await axios.get(
`/get_dispensers/?role=${role}${
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
}
if (isMounted) {
const rawOptions = Array.isArray(response?.data?.results)
? response?.data?.results
: Array.isArray(response?.data)
? response?.data
: Array.isArray(response?.data?.data)
? response?.data?.data
: Array.isArray(response)
? response
: [];
// Normalize the data - both delegates and dispensers have the same structure
const normalizedOptions = rawOptions.map((item) => {
const fullname = item?.fullname || "-";
const mobile = item?.mobile || "";
const mobileLabel = mobile ? ` (${mobile})` : "";
const label = `${fullname}${mobileLabel}`;
return {
...item,
label: label || "-",
};
});
setOptions(normalizedOptions);
}
} catch (error) {
if (isMounted) {
setFetchError("دریافت لیست کاربران با خطا مواجه شد.");
console.error("Error fetching users:", error);
}
} finally {
if (isMounted) {
setLoading(false);
}
}
};
fetchUsers();
return () => {
isMounted = false;
};
}, [userType, selectedSubUser?.key]);
const handleCloseModal = () => {
dispatch(CLOSE_MODAL());
};
const getSubAccessLevels = (user) => {
if (!user) return [];
const subAccessLevels = [];
// Common excluded boolean fields that are not access levels
const excludedFields = [
"active",
"trash",
"deleted",
"isActive",
"isDeleted",
];
// Get all boolean fields that represent sub access levels
// These are fields in the object that are boolean and indicate sub access levels
Object.keys(user).forEach((key) => {
if (typeof user[key] === "boolean" && user[key] === true) {
// Exclude common boolean fields that aren't access levels
if (!excludedFields.includes(key.toLowerCase())) {
// Use getFaUserRole to get the Persian translation
const translatedLabel = getFaUserRole(key);
subAccessLevels.push({
key: key,
label: translatedLabel || key,
});
}
}
});
return subAccessLevels;
};
const handleSubmit = async () => {
if (!selectedUser) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg: "لطفاً یک کاربر را انتخاب کنید.",
});
return;
}
if (!device?.key && !device?.id) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg: "شناسه دستگاه یافت نشد. لطفاً دوباره تلاش کنید.",
});
return;
}
setSubmitting(true);
try {
const payload = {
key: device?.key || device?.id,
recipient_type:
userType === "delegate" ? "representative" : "dispenser",
recipient_key: selectedUser?.key || selectedUser?.id,
};
await axios.put("/user-pos-machine/0/", payload);
openNotif({
vertical: "top",
horizontal: "center",
severity: "success",
msg: "دستگاه با موفقیت به کاربر فرعی اختصاص داده شد.",
});
if (onSuccess) {
onSuccess();
}
handleCloseModal();
} catch (error) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg:
error?.response?.data?.result ||
error?.response?.data?.detail ||
"اختصاص دستگاه با خطا مواجه شد.",
});
} finally {
setSubmitting(false);
}
};
const selectedUserSubAccessLevels = selectedUser
? getSubAccessLevels(selectedUser)
: [];
return (
<Grid
container
direction="column"
gap={SPACING.SMALL}
width="100%"
alignItems="stretch"
sx={{ minWidth: 400 }}
>
<Typography variant="body2">
دستگاه انتخاب شده:{" "}
{device?.serial ||
device?.pos_unique_id ||
device?.pos_id ||
device?.posId ||
"-"}
</Typography>
<LabelField label="نوع کاربر">
<FormControl fullWidth>
<RadioGroup
row
sx={{ gap: 2 }}
value={userType}
onChange={(e) => setUserType(e.target.value)}
>
<FormControlLabel
value="delegate"
control={<Radio />}
label="نماینده‌ها"
/>
<FormControlLabel
value="dispenser"
control={<Radio />}
label="پخش‌کنندگان"
/>
</RadioGroup>
</FormControl>
</LabelField>
{loading ? (
<Grid container justifyContent="center">
<CircularProgress size={24} />
</Grid>
) : (
<Autocomplete
disablePortal
fullWidth
size="small"
options={options}
value={selectedUser}
loading={loading}
loadingText="در حال دریافت..."
noOptionsText="موردی یافت نشد."
onChange={(event, value) => {
setSelectedUser(value);
setSelectedAccessLevel("");
}}
isOptionEqualToValue={(option, value) =>
option?.key === value?.key || option?.id === value?.id
}
getOptionLabel={(option) => option?.label || ""}
renderOption={(props, option) => {
if (!option) return null;
return (
<li {...props} key={option?.key || option?.id}>
<Grid container direction="column">
<Typography variant="body2" fontWeight={600}>
{option?.label || "-"}
</Typography>
{option?.city && (
<Typography variant="caption" color="text.secondary">
{option.city}
</Typography>
)}
</Grid>
</li>
);
}}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب کاربر"
placeholder="نام یا شماره تماس"
InputProps={{
...params.InputProps,
endAdornment: (
<>
{loading ? (
<CircularProgress color="inherit" size={16} />
) : null}
{params.InputProps.endAdornment}
</>
),
}}
/>
)}
/>
)}
{fetchError && (
<Typography variant="caption" color="error">
{fetchError}
</Typography>
)}
{selectedUser && selectedUserSubAccessLevels.length > 0 && (
<LabelField label="سطح دسترسی فرعی">
<Select
fullWidth
size="small"
value={selectedAccessLevel}
onChange={(e) => setSelectedAccessLevel(e.target.value)}
displayEmpty
>
<MenuItem value="">
<em>انتخاب سطح دسترسی</em>
</MenuItem>
{selectedUserSubAccessLevels.map((level) => (
<MenuItem key={level.key} value={level.key}>
{level.label}
</MenuItem>
))}
</Select>
</LabelField>
)}
<Grid container justifyContent="flex-end" gap={SPACING.SMALL} mt={2}>
<Button
variant="outlined"
color="primary"
onClick={handleCloseModal}
disabled={submitting}
>
انصراف
</Button>
<Button
variant="contained"
onClick={handleSubmit}
disabled={!selectedUser || submitting || loading}
>
{submitting ? "در حال ثبت..." : "ثبت"}
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,266 @@
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import {
Box,
Grid,
IconButton,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
Typography,
Button,
} from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import PersonAddAlt1RoundedIcon from "@mui/icons-material/PersonAddAlt1Rounded";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import { AppContext } from "../../../../contexts/AppContext";
import {
LOADING_START,
LOADING_END,
CLOSE_MODAL,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { AssignSubUserModal } from "./AssignSubUserModal";
import { AddAccessLevelModal } from "./AddAccessLevelModal";
import { EditSubSectionsModal } from "./EditSubSectionsModal";
export const DeviceOperations = ({
device,
onSubUserAssigned,
fetchApiData,
page,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleAssignSubUser = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "اختصاص دستگاه به کاربر فرعی",
width: "auto",
content: (
<AssignSubUserModal device={device} onSuccess={onSubUserAssigned} />
),
})
);
};
const handleAddAccessLevel = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "افزودن سطح دسترسی",
width: "auto",
content: (
<AddAccessLevelModal device={device} onSuccess={onSubUserAssigned} />
),
})
);
};
const handleEditSubSections = (accessLevel) => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش زیربخش‌های سطح دسترسی",
width: "auto",
content: (
<EditSubSectionsModal
accessLevel={accessLevel}
device={device}
onSuccess={() => fetchApiData(page)}
/>
),
})
);
};
const handleDeleteAccessLevel = (accessLevel) => {
handleClose();
dispatch(
OPEN_MODAL({
title: "حذف سطح دسترسی",
size: 320,
content: (
<Grid container spacing={2}>
<Grid
item
xs={12}
container
justifyContent="center"
alignItems="center"
mb={2}
>
<Typography variant="body2" color="default">
آیا از حذف سطح دسترسی{" "}
{getFaUserRole(accessLevel?.name) || accessLevel?.name} مطمئن
هستید؟
</Typography>
</Grid>
<Grid item xs={6}>
<Button
fullWidth
variant="contained"
color="error"
onClick={async () => {
try {
dispatch(LOADING_START());
await axios.delete(
`/pos-access-level/0/?key=${accessLevel?.key}`
);
dispatch(LOADING_END());
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
severity: "success",
msg: "سطح دسترسی با موفقیت حذف شد.",
});
if (fetchApiData) {
fetchApiData(page);
}
} catch (error) {
dispatch(LOADING_END());
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg:
error?.response?.data?.result ||
error?.response?.data?.detail ||
"حذف سطح دسترسی با خطا مواجه شد.",
});
}
}}
>
تایید
</Button>
</Grid>
<Grid item xs={6}>
<Button
fullWidth
variant="outlined"
onClick={() => dispatch(CLOSE_MODAL())}
>
انصراف
</Button>
</Grid>
</Grid>
),
})
);
};
const open = Boolean(anchorEl);
const id = open ? "device-operations-popover" : undefined;
const accessLevels = Array.isArray(device?.accessLevels)
? device.accessLevels.filter((level) => !level?.trash)
: [];
return (
<>
<IconButton
aria-describedby={id}
color="primary"
onClick={handleOpen}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<List sx={{ minWidth: 200, p: 0 }}>
<ListItemButton onClick={handleAssignSubUser}>
<ListItemIcon sx={{ minWidth: 36, color: "success.main" }}>
<PersonAddAlt1RoundedIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="اختصاص به کاربر"
primaryTypographyProps={{
sx: { color: "success.main", fontSize: "0.875rem" },
}}
/>
</ListItemButton>
<ListItemButton onClick={handleAddAccessLevel}>
<ListItemIcon sx={{ minWidth: 36, color: "info.main" }}>
<AddIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary="افزودن سطح دسترسی"
primaryTypographyProps={{
sx: { color: "info.main", fontSize: "0.875rem" },
}}
/>
</ListItemButton>
{accessLevels.length > 0 && (
<>
<Box sx={{ pl: 1.5, py: 0.5, bgcolor: "action.hover" }}>
<Typography variant="caption" color="text.secondary">
مدیریت سطح دسترسی:
</Typography>
</Box>
{accessLevels.map((level, idx) => (
<Box key={level?.key || idx} sx={{ pl: 1, pr: 0.5 }}>
<Grid
container
alignItems="center"
justifyContent="space-between"
>
<Typography variant="body2" sx={{ py: 0.5, pl: 0.5 }}>
{getFaUserRole(level?.name) || level?.name || "-"}
</Typography>
<Grid gap={0.5}>
<IconButton
size="small"
color="primary"
onClick={() => handleEditSubSections(level)}
>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
color="error"
onClick={() => handleDeleteAccessLevel(level)}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Grid>
</Grid>
</Box>
))}
</>
)}
</List>
</Popover>
</>
);
};

View File

@@ -0,0 +1,202 @@
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import {
Button,
Checkbox,
FormControlLabel,
Grid,
Typography,
} from "@mui/material";
import axios from "axios";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { LabelField } from "../../../../components/label-field/LabelField";
import { SPACING } from "../../../../data/spacing";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
export const EditSubSectionsModal = ({ accessLevel, device, onSuccess }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [submitting, setSubmitting] = useState(false);
const [values, setValues] = useState({
in_province_sale: accessLevel?.in_province_sale || false,
out_province_sale: accessLevel?.out_province_sale || false,
cutting: accessLevel?.cutting || false,
freezing: accessLevel?.freezing || false,
warehouse: accessLevel?.warehouse || false,
retail: accessLevel?.retail || false,
});
const handleCloseModal = () => {
dispatch(CLOSE_MODAL());
};
const handleChange = (field) => (event) => {
setValues((prev) => ({
...prev,
[field]: event.target.checked,
}));
};
const handleSubmit = async () => {
if (!accessLevel?.key) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg: "شناسه سطح دسترسی یافت نشد. لطفاً دوباره تلاش کنید.",
});
return;
}
setSubmitting(true);
try {
const payload = {
key: accessLevel.key,
in_province_sale: values.in_province_sale,
out_province_sale: values.out_province_sale,
cutting: values.cutting,
freezing: values.freezing,
warehouse: values.warehouse,
retail: values.retail,
};
await axios.put("/pos-access-level/0/", payload);
openNotif({
vertical: "top",
horizontal: "center",
severity: "success",
msg: "زیربخش‌های سطح دسترسی با موفقیت ویرایش شد.",
});
if (onSuccess) {
onSuccess();
}
handleCloseModal();
} catch (error) {
openNotif({
vertical: "top",
horizontal: "center",
severity: "error",
msg:
error?.response?.data?.result ||
error?.response?.data?.detail ||
"ویرایش زیربخش‌ها با خطا مواجه شد.",
});
} finally {
setSubmitting(false);
}
};
return (
<Grid
container
direction="column"
gap={SPACING.SMALL}
width="100%"
alignItems="stretch"
sx={{ minWidth: 400 }}
>
<Typography variant="body2">
سطح دسترسی:{" "}
{getFaUserRole(accessLevel?.name) || accessLevel?.name || "-"}
</Typography>
<Typography variant="body2">
دستگاه:{" "}
{device?.serial ||
device?.pos_unique_id ||
device?.pos_id ||
device?.posId ||
"-"}
</Typography>
<LabelField label="تنظیمات زیربخش‌ها">
<Grid container p={1}>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.in_province_sale}
onChange={handleChange("in_province_sale")}
/>
}
label="فروش داخل استان"
/>
</Grid>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.out_province_sale}
onChange={handleChange("out_province_sale")}
/>
}
label="فروش خارج استان"
/>
</Grid>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.cutting}
onChange={handleChange("cutting")}
/>
}
label="قطعه بندی"
/>
</Grid>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.freezing}
onChange={handleChange("freezing")}
/>
}
label="انجماد"
/>
</Grid>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.warehouse}
onChange={handleChange("warehouse")}
/>
}
label="انبار"
/>
</Grid>
<Grid item xs={6}>
<FormControlLabel
control={
<Checkbox
checked={values.retail}
onChange={handleChange("retail")}
/>
}
label="خرده‌فروشی"
/>
</Grid>
</Grid>
</LabelField>
<Grid container justifyContent="flex-end" gap={SPACING.SMALL} mt={2}>
<Button
variant="outlined"
color="primary"
onClick={handleCloseModal}
disabled={submitting}
>
انصراف
</Button>
<Button
variant="contained"
onClick={handleSubmit}
disabled={submitting}
>
{submitting ? "در حال ثبت..." : "ثبت"}
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,284 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import axios from "axios";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { BackButton } from "../../../../components/back-button/BackButton";
import { Autocomplete, Button, TextField, Box, Chip } from "@mui/material";
import { RiSearchLine } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { formatJustDate } from "../../../../utils/formatTime";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { DeviceOperations } from "./DeviceOperations";
import { Lock } from "@mui/icons-material";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const GuildPspDevices = () => {
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 [brands, setBrands] = useState([]);
const [selectedBrand, setSelectedBrand] = useState("");
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const dispatch = useDispatch();
const fetchBrands = async () => {
try {
dispatch(LOADING_START());
const response = await axios.get(
`/get_all_pos_company/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
setBrands(response.data);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching brands:", error);
dispatch(LOADING_END());
}
};
const canDoAction = (item) => {
if (item?.owner?.role?.includes("KillHouse")) {
return getRoleFromUrl() === "KillHouse";
}
if (item?.owner?.includes("Steward")) {
return getRoleFromUrl() === "Steward";
}
return false;
};
const fetchApiData = async (page) => {
try {
dispatch(LOADING_START());
let url = `/user-pos-machine/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=${page}&page_size=${perPage}`;
if (selectedBrand) {
url += `&company=${selectedBrand}`;
}
const response = await axios.get(url);
setData(response.data?.results || []);
setTotalRows(response.data?.count || 0);
} catch (error) {
console.error("Error fetching devices:", error);
} finally {
dispatch(LOADING_END());
}
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
const d = data?.map((item, i) => {
const owner = item?.owner || {};
const posCompany = item?.posCompany || {};
// Format recipient information
const recipientDisplay = item?.recipient
? `${item?.recipient?.firstName || ""} ${
item?.recipient?.lastName || ""
}`.trim() +
(item?.recipient?.mobile
? ` (${item?.recipient?.mobile} - ${
item?.recipient?.agentType === "dispenser"
? "توزیع کننده"
: "نماینده"
})`
: "")
: "-";
// Format access_levels to display as chips only (filter out trash items)
const activeAccessLevels = Array.isArray(item?.accessLevels)
? item.accessLevels.filter((level) => !level?.trash)
: [];
const accessLevelsDisplay =
activeAccessLevels.length > 0 ? (
<Grid container gap={0.5} flexWrap="wrap">
{activeAccessLevels.map((level, idx) => (
<Chip
key={level?.key || idx}
label={getFaUserRole(level?.name) || level?.name || "-"}
size="small"
/>
))}
</Grid>
) : (
"-"
);
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
posCompany?.name || "-",
item?.serial ?? "-",
item?.receiverNumber ?? "-",
item?.terminalNumber ?? "-",
item?.password ?? "-",
item?.posUniqueId || item?.posId || "-",
owner?.fullname ? `${owner?.fullname} (${owner?.mobile ?? "-"})` : "-",
owner?.nationalId || owner?.nationalCode || "-",
item?.createDate ? formatJustDate(item?.createDate) : "-",
accessLevelsDisplay,
recipientDisplay,
item?.active ? "فعال" : "غیرفعال",
canDoAction(item) ? (
<DeviceOperations
key={item?.id || item?.key || `device-${i}`}
device={item}
/>
) : (
<Lock sx={{ fontSize: 20, mt: 1, color: "gray" }} />
),
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchBrands();
}, [dispatch, selectedSubUser?.key]);
useEffect(() => {
fetchApiData(page);
}, [selectedBrand, perPage, selectedSubUser?.key]);
const handleSubmit = async (event) => {
event.preventDefault();
fetchApiData(1);
};
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
mb={1}
padding={2}
width="100%"
>
<Grid
container
width="100%"
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
>
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
xs={12}
md="auto"
>
<Autocomplete
size="small"
style={{ minWidth: 210 }}
disablePortal
id="brand"
options={
Array.isArray(brands)
? brands.map((i) => {
return {
data: i,
label: `${i?.name || ""}`,
};
})
: []
}
onChange={(event, value) => {
setSelectedBrand(value?.data?.key);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب برند دستگاه" />
)}
/>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</Grid>
</Grid>
</Grid>
);
return (
<Box display="flex" justifyContent="center">
<Grid container xs={12} lg={10}>
<BackButton />
{tableTitle}
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"برند دستگاه",
"شماره سریال",
"شماره پذیرنده",
"شماره ترمینال",
"کلمه عبور امنیتی",
"کلید پوز",
"مالک",
"کدملی",
"تاریخ ایجاد",
"سطوح دسترسی",
"تحویل گیرنده",
"فعال / غیرفعال",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="مدیریت دستگاه ها"
/>
</Grid>
</Box>
);
};
export default GuildPspDevices;

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildDeleteFreeBar = createAsyncThunk(
"GUILD_DELETE_FREE_BAR",
async (id, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete(
"/steward_free_bar/0/?key=" + id
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

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 guildAllocatedStockOperationService = createAsyncThunk(
"GUILD_ALLOCATED_STOCK_OPERATION_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put("steward-allocation/0/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,21 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildAuthCodeSubmitService = createAsyncThunk(
"GUILD_AUTH_CODE_SUBMIT_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put(
"steward-guild-allocation/0/",
d
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildDeleteOutOfProvinceSell = createAsyncThunk(
"GUILD_DELETE_OUT_OF_PROVINCE_SELL",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete(
`steward_free_sale_bar/0/?key=${d}`
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

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 guildEditFreeBar = createAsyncThunk(
"GUILD_EDIT_FREE_BAR",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put("steward_free_bar/0/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

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 guildGetFreeBars = createAsyncThunk(
"GUILD_GET_FREE_BARS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward_free_bar/", {
params: { date: d.date },
});
dispatch(LOADING_END());
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 guildGetFreeSaleBarService = createAsyncThunk(
"GUILD_GET_FREE_SALE_BARS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward_free_sale_bar/", {
params: d,
});
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";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildGetStewardsService = createAsyncThunk(
"GUILD_GET_GUILDS_SERVICE",
async (id, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
"guilds/?other_guilds_for_steward=true/",
{
params: { role: getRoleFromUrl() },
}
);
dispatch(LOADING_END());
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 guildGetInventoryAllocatedService = createAsyncThunk(
"GUILD_GET_INVENTORY_ALLOCATED_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward-allocation/", {
params: { date: d.date, steward: true, role_key: d.role_key || "" },
});
dispatch(LOADING_END());
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 guildGetInventoryStockService = createAsyncThunk(
"GUILD_GET_INVENTORY_STOCK_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward_warehouse/", {
params: { date: d.date, role_key: d.role_key || "" },
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,10 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
export const guildGetProfile = createAsyncThunk(
"GUILD_GET_PROFILE",
async () => {
const { data, status } = await axios.get("guilds/0/?profile");
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 guildGetStewards = createAsyncThunk(
"GUILD_GET_STEWARDS",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("guilds/", {
params: {
date: d.date,
steward_sub_guilds: true,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,24 @@
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 guildSalesInfoDashboardService = createAsyncThunk(
"GUILD_SALES_INFO_DASHBOARD_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.get("guild-sales-info-dashboard/", {
params: {
role: d.role || getRoleFromUrl(),
role_key: d.role_key || "",
},
});
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response?.data?.result };
}
}
);

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 guildSubmitFreeBar = createAsyncThunk(
"GUILD_SUBMIT_FREE_BAR",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.post("steward_free_bar/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

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 guildUpdateAllocatedStockService = createAsyncThunk(
"GUILD_UPDATE_ALLOCATED_STOCK_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put("steward-allocation/0/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,17 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildDeleteAllocatedService = createAsyncThunk(
"GUILD_DELETE_ALLOCATED_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.delete("steward-guild-allocation/0/", {
params: {
steward_allocation_key: d.steward_allocation_key,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,21 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const GuildEditAllocateStewardService = createAsyncThunk(
"GUILD_EDIT_ALLOCATE_STEWARD_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put(
"steward-guild-allocation/0/",
d
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildGetAllocationData = createAsyncThunk(
"GUILD_GET_ALLOCATION_DATA",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
"steward-allocation/?steward_guilds_allocations=true/",
{
params: {
date: d.date,
},
}
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,21 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const guildInventoryFinalSubmitService = createAsyncThunk(
"GUILD_INVENTORY_FINAL_SUBMIT_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put(
"steward-guild-allocation/0/",
d
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

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 senfGetAllocationDashboardService = createAsyncThunk(
"SENF_GET_ALLOCATION_DASHBOARD_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("guild-allocation-dashbord/", {
params: d,
});
dispatch(LOADING_END());
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";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const senfGetInventoryAllocatedService = createAsyncThunk(
"SENF_GET_INVENTORY_ALLOCATED_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward-allocation/", {
params: {
date1: d.date1,
date2: d.date2,
type: d.type || "",
role_key: d.role_key || "",
role: getRoleFromUrl(),
search: d.search,
},
});
dispatch(LOADING_END());
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 senfGetInventoryStockService = createAsyncThunk(
"SENF_GET_INVENTORY_STOCK_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("guilds_warehouse/", {
params: d,
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

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

View File

@@ -0,0 +1,18 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const stawardGetSegmentDashboardService = createAsyncThunk(
"STEWARD_GET_SEGMENT_DASHBOARD_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("segmentation-dashboard/", {
params: {
search: "filter",
...d,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,19 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const stawardGetOutDashboardService = createAsyncThunk(
"STEWARD-GET-OUT_DASHBOARD_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("steward_free_bar_dashboard", {
params: {
...d,
role: getRoleFromUrl(),
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,17 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const stawardGetSegmantRoleService = createAsyncThunk(
"STEWARD_GET_SEGMANT_ROLE_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("guilds/?&all=true", {
params: {
...d,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,19 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const stewardGetOutSellService = createAsyncThunk(
"STEWRD_GET_OUT_SELL_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("roles-products", {
params: {
role: getRoleFromUrl(),
...d,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const stewardDeleteSegmentService = createAsyncThunk(
"STEWARD_DELETE_SEGMENT",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete(
`app-segmentation/0/?key=${d}`
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e };
}
}
);

View File

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

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 stewardSegmentSubmitService = createAsyncThunk(
"STEWARD_SUBMIT_SEGMANT",
async (d, { dispatch }) => {
try {
dispatch(LOADING_START());
const { data, status } = await axios.post("app-segmentation/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
export const stewardDeleteOutSellService = createAsyncThunk(
"STEWARD_DELETE_OUT_OF_PROVINCE_SELL",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete(
`steward_free_sale_bar/0/?key=${d}`
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e };
}
}
);

View File

@@ -0,0 +1,41 @@
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import { createAsyncThunk } from "@reduxjs/toolkit";
export const stewardSellOutGetBuyers = createAsyncThunk(
"STEWARD_GET_BUYERS_SELL_OUT",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("out-province-carcasses-buyer/", {
params: {
role: getRoleFromUrl(),
role_key: d?.role_key || "",
mobile: d?.mobile,
},
});
dispatch(LOADING_END());
return { data, status };
}
);
export const stewatdSubmitBuyerDataService = createAsyncThunk(
"STEWARD_SUBMIT_BUYER_SERVICE",
async (d, { dispatch }) => {
try {
dispatch(LOADING_START());
const { data, status } = await axios.post(
"out-province-carcasses-buyer/",
{
...d,
}
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response.data.result };
}
}
);

View File

@@ -0,0 +1,24 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const stewardSellOutGetDashboard = createAsyncThunk(
"SLAUGHTRE_SELL_DASHBOARD",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get(
`steward_free_sale_bar_dashboard`,
{
params: {
date1: d.selectedDate1,
date2: d.selectedDate2,
role: getRoleFromUrl(),
role_key: d.role_key || "",
},
}
);
dispatch(LOADING_END());
return { data, status };
}
);

View File

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

View File

@@ -0,0 +1,50 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const stewardSubmitFreeBarService = createAsyncThunk(
"STEWARD_FREE_BAR_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.post("steward_free_bar/", d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response?.data?.result };
}
}
);
export const stewardEditFreeBarService = createAsyncThunk(
"STEWARD_EDIT_FREE_BAR_SERVICE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.put(`steward_free_bar/0/`, d);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response?.data?.result };
}
}
);
export const stewardDeleteFreeBarService = createAsyncThunk(
"STEWARD_DELETE_FREE_BAR_SERVICE",
async (key, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.delete(
`steward_free_bar/0/?key=${key}`
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response?.data?.result || "خطا در حذف اطلاعات" };
}
}
);