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,769 @@
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
FormLabel,
InputAdornment,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { fixBase64 } from "../../../../utils/toBase64";
import { slaughterEditFreeSaleService } from "../../services/slaughter-edit-free-sale";
import { slaughterSubmitFreeSaleService } from "../../services/slaughter-submit-free-sale";
import {
slaughterGetExclusiveKillersService,
slaughterGetProductsService,
} from "../../services/slaughter-inventory-gets";
import moment from "moment";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../services/slaughter-get-provinces";
import { CarPelak } from "../../../../components/car-pelak/CarPelak";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const ValidationSchemaAlive = Yup.object().shape({
poultry_name: Yup.string().required("نام مرغدار الزامی است"),
poultry_mobile: Yup.string()
.required("شماره موبایل مرغدار الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
province: Yup.string().required("استان الزامی است"),
city: Yup.string().required("شهر الزامی است"),
clearance_code: Yup.string().required("کد قرنطینه الزامی است"),
quantity: Yup.number()
.required("تعداد الزامی است")
.min(1, "تعداد باید بیشتر از 0 باشد"),
live_weight: Yup.number()
.required("وزن الزامی است")
.min(0.01, "وزن باید بیشتر از 0 باشد"),
bar_image: Yup.string().required("عکس بار الزامی است"),
});
const ValidationSchemaCarcass = Yup.object().shape({
poultry_name: Yup.string().required("نام مرغدار الزامی است"),
poultry_mobile: Yup.string()
.required("شماره موبایل مرغدار الزامی است")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
province: Yup.string().required("استان الزامی است"),
city: Yup.string().required("شهر الزامی است"),
clearance_code: Yup.string().required("کد قرنطینه الزامی است"),
bar_image: Yup.string().required("عکس بار الزامی است"),
number_of_carcasses: Yup.number()
.required("حجم لاشه الزامی است")
.min(1, "حجم لاشه باید بیشتر از 0 باشد"),
weight_of_carcasses: Yup.number()
.required("وزن لاشه الزامی است")
.min(0.01, "وزن باید بیشتر از 0 باشد"),
});
const DriverSchema = Yup.object().shape({
driverName: Yup.string(),
driverMobile: Yup.string()
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
});
const ProvinceCityFields = ({ formik, cities, provinces, isEdit }) => (
<>
<Autocomplete
fullWidth
disablePortal
id="province"
options={provinces.map((p) => ({ id: p.name, label: p.name }))}
value={
formik.values.province
? { id: formik.values.province, label: formik.values.province }
: null
}
onChange={(e, value) => {
formik.setFieldValue("province", value ? value.id : "");
formik.setFieldValue("city", "");
}}
renderInput={(params) => (
<TextField
{...params}
label="استان را انتخاب کنید"
error={formik.touched.province && Boolean(formik.errors.province)}
helperText={formik.touched.province && formik.errors.province}
/>
)}
/>
{isEdit && (
<Typography variant="caption" color="error">
استان: {formik.values.province}
</Typography>
)}
<Autocomplete
fullWidth
disablePortal
id="city"
disabled={!formik.values.province}
options={cities.map((c) => ({ id: c.name, label: c.name }))}
value={
formik.values.city
? { id: formik.values.city, label: formik.values.city }
: null
}
onChange={(e, value) => {
formik.setFieldValue("city", value ? value.id : "");
}}
renderInput={(params) => (
<TextField
{...params}
label="شهر را انتخاب کنید"
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
/>
)}
/>
{isEdit && (
<Typography variant="caption" color="error">
شهر: {formik.values.city}
</Typography>
)}
</>
);
const AliveFormSection = ({ formik, cities, provinces, isEdit, item }) => (
<>
<TextField
id="poultry_name"
name="poultry_name"
label="نام مرغدار"
value={formik.values.poultry_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.poultry_name && Boolean(formik.errors.poultry_name)}
helperText={formik.touched.poultry_name && formik.errors.poultry_name}
/>
<TextField
id="poultry_mobile"
name="poultry_mobile"
label="تلفن مرغداری"
value={formik.values.poultry_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.poultry_mobile && Boolean(formik.errors.poultry_mobile)
}
helperText={formik.touched.poultry_mobile && formik.errors.poultry_mobile}
/>
<ProvinceCityFields
formik={formik}
cities={cities}
provinces={provinces}
isEdit={isEdit}
item={item}
/>
<TextField
id="clearance_code"
name="clearance_code"
label="کد قرنطینه"
disabled={isEdit && item?.registerType === "automatic"}
value={formik.values.clearance_code}
onChange={(e) =>
formik.setFieldValue("clearance_code", e.target.value.toUpperCase())
}
onBlur={formik.handleBlur}
error={
formik.touched.clearance_code && Boolean(formik.errors.clearance_code)
}
helperText={formik.touched.clearance_code && formik.errors.clearance_code}
/>
<TextField
id="quantity"
name="quantity"
label="حجم زنده"
InputProps={{
endAdornment: <InputAdornment position="end">قطعه</InputAdornment>,
}}
value={formik.values.quantity}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.quantity && Boolean(formik.errors.quantity)}
helperText={formik.touched.quantity && formik.errors.quantity}
/>
<TextField
id="live_weight"
name="live_weight"
label="وزن بار زنده"
InputProps={{
endAdornment: <InputAdornment position="end">کیلوگرم</InputAdornment>,
}}
value={formik.values.live_weight}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.live_weight && Boolean(formik.errors.live_weight)}
helperText={formik.touched.live_weight && formik.errors.live_weight}
/>
</>
);
const CarcassFormSection = ({ formik, cities, provinces, isEdit, item }) => (
<>
<TextField
id="poultry_name"
name="poultry_name"
label="نام فروشنده"
value={formik.values.poultry_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.poultry_name && Boolean(formik.errors.poultry_name)}
helperText={formik.touched.poultry_name && formik.errors.poultry_name}
/>
<TextField
id="poultry_mobile"
name="poultry_mobile"
label="تلفن فروشنده"
value={formik.values.poultry_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.poultry_mobile && Boolean(formik.errors.poultry_mobile)
}
helperText={formik.touched.poultry_mobile && formik.errors.poultry_mobile}
/>
<ProvinceCityFields
formik={formik}
cities={cities}
provinces={provinces}
isEdit={isEdit}
item={item}
/>
<TextField
id="clearance_code"
name="clearance_code"
label="کد قرنطینه"
value={formik.values.clearance_code}
onChange={(e) =>
formik.setFieldValue("clearance_code", e.target.value.toUpperCase())
}
onBlur={formik.handleBlur}
error={
formik.touched.clearance_code && Boolean(formik.errors.clearance_code)
}
helperText={formik.touched.clearance_code && formik.errors.clearance_code}
/>
<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
}
/>
<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
}
/>
</>
);
const CommonFields = ({ formikDriver, setDriverPelak }) => (
<>
<CarPelak
width="100%"
handleChange={(pelak1, pelak2, pelak3, pelak4) => {
setDriverPelak([pelak1, pelak2, pelak3, pelak4]);
}}
/>
<TextField
label="نام و نام خانوادگی راننده"
name="driverName"
value={formikDriver.values.driverName}
onChange={formikDriver.handleChange}
error={
formikDriver.touched.driverName &&
Boolean(formikDriver.errors.driverName)
}
helperText={
formikDriver.touched.driverName && formikDriver.errors.driverName
}
fullWidth
/>
<TextField
label="تلفن راننده"
name="driverMobile"
value={formikDriver.values.driverMobile}
onChange={formikDriver.handleChange}
onBlur={formikDriver.handleBlur}
error={
formikDriver.touched.driverMobile &&
Boolean(formikDriver.errors.driverMobile)
}
helperText={
formikDriver.touched.driverMobile && formikDriver.errors.driverMobile
}
fullWidth
/>
</>
);
export const SlaughterSubmitFreeBar = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [buyType, setBuyType] = useState(
item ? (item?.buyType === "live" ? "alive" : "carcasses") : "alive"
);
const [dayEnterInventory, setDayEnterInventory] = useState("today");
const [productKey, setProductKey] = useState(null);
const [killerKey, setKillerKey] = useState(null);
const [productData, setProductData] = useState([]);
const [killersData, setKillersData] = useState([]);
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const [profileImages, setProfileImages] = useState([]);
const [driverPelak, setDriverPelak] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const formik = useFormik({
initialValues: {
buyType: item
? item?.buyType === "live"
? "alive"
: "carcasses"
: "alive",
poultry_name: item ? item.poultryName : "",
poultry_mobile: item ? item.poultryMobile : "",
province: item ? item.province : "",
city: item ? item.city : "",
clearance_code: item ? item.barClearanceCode : "",
quantity: item ? item.quantity : "",
live_weight: item ? item.liveWeight : "",
bar_image: item ? " " : "",
number_of_carcasses: item ? item.numberOfCarcasses : "",
weight_of_carcasses: item ? item.weightOfCarcasses : "",
},
validationSchema: Yup.lazy((values) =>
values.buyType === "alive"
? ValidationSchemaAlive
: ValidationSchemaCarcass
),
onSubmit: async (values) => {
try {
const payload = {
driver_name: formikDriver.values.driverName,
driver_mobile: formikDriver.values.driverMobile,
poultry_name: values.poultry_name,
poultry_mobile: values.poultry_mobile,
province: values.province,
city: values.city,
...(values.clearance_code !== item?.barClearanceCode && {
bar_clearance_code: values.clearance_code,
}),
bar_image: values.bar_image,
killer_key: killerKey ? killerKey : null,
date:
dayEnterInventory === "today"
? moment(new Date()).format("YYYY-MM-DD")
: moment(new Date()).add(1, "days").format("YYYY-MM-DD"),
buy_type: values.buyType === "alive" ? "live" : "carcass",
product_key: productKey?.key,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
car: driverPelak.join(" "),
...(values.buyType === "alive"
? {
quantity: Number(values.quantity),
live_weight: Number(values.live_weight),
}
: {
number_of_carcasses: values.number_of_carcasses,
weight_of_carcasses: values.weight_of_carcasses,
}),
};
const response = item
? await dispatch(
slaughterEditFreeSaleService({ ...payload, key: item.key })
)
: await dispatch(slaughterSubmitFreeSaleService(payload));
if (response.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: response.payload.error,
severity: "error",
});
} else {
if (updateTable) {
updateTable();
}
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: item
? "اطلاعات با موفقیت ویرایش شد"
: "اطلاعات خرید شما با موفقیت ثبت شد",
severity: "success",
});
}
} catch (error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "خطا در ارسال اطلاعات",
severity: "error",
});
}
},
validateOnMount: true,
});
const formikDriver = useFormik({
initialValues: {
driverName: item?.driverName || "",
driverMobile: item?.driverMobile || "",
},
validationSchema: DriverSchema,
});
const handleBuyTypeChange = (event) => {
setBuyType(event.target.value);
formik.setFieldValue("buyType", event.target.value);
};
const handleChangeDayEnterInventory = (event) => {
setDayEnterInventory(event.target.value);
};
const factorPaymentHandler = (imageList) => {
if (imageList[0]) {
formik.setFieldValue("bar_image", fixBase64(imageList[0]?.data_url));
}
setProfileImages(imageList);
};
useEffect(() => {
const loadInitialData = async () => {
try {
const [productsRes, killersRes, provincesRes] = await Promise.all([
dispatch(
slaughterGetProductsService({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
),
dispatch(slaughterGetExclusiveKillersService()),
dispatch(slaughterGetProvinceService()),
]);
const productsData = productsRes.payload?.data;
if (Array.isArray(productsData)) {
setProductData(productsData);
} else if (productsData?.data && Array.isArray(productsData.data)) {
setProductData(productsData.data);
} else {
setProductData([]);
}
setKillersData(killersRes.payload.data || []);
setProvinceData(provincesRes.payload.data || []);
if (item?.province) {
const citiesRes = await dispatch(
slaughterGetCitiesService(item.province)
);
setCityData(citiesRes.payload.data || []);
}
} catch (error) {
console.error(error);
}
};
loadInitialData();
}, [dispatch, selectedSubUser?.key]);
useEffect(() => {
if (formik.values.province) {
dispatch(slaughterGetCitiesService(formik.values.province)).then((r) => {
setCityData(r.payload.data || []);
});
}
}, [formik.values.province, dispatch]);
return (
<Grid container direction="column" justifyContent="center" gap={2}>
{item?.registerType !== "automatic" ? (
<Grid container direction="column" justifyContent="center" gap={2}>
<RadioGroup
value={buyType}
onChange={handleBuyTypeChange}
row
aria-labelledby="buy-type-radio-group"
name="buy-type-radio-group"
>
<FormControlLabel
disabled={!!item}
value="alive"
control={<Radio />}
label="مرغ زنده"
/>
<FormControlLabel
disabled={!!item}
value="carcasses"
control={<Radio />}
label="لاشه"
/>
</RadioGroup>
{!item && (
<Grid xs={12} container gap={2}>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="product-select"
options={
Array.isArray(productData) && productData.length > 0
? productData.map((i) => ({
data: i,
label: i.name || "",
}))
: []
}
onChange={(event, value) => setProductKey(value?.data || null)}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
)}
{!!killersData.length && (
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="killer-select"
options={killersData.map((i) => ({
data: i.key,
label: `${i.name} (${i?.phone})`,
}))}
onChange={(event, value) => setKillerKey(value?.data)}
renderInput={(params) => (
<TextField
fullWidth
{...params}
label="انتخاب کشتارکن (اختیاری)"
/>
)}
/>
)}
<form
onSubmit={formik.handleSubmit}
style={{
display: "flex",
flexDirection: "column",
gap: SPACING.LARGE,
}}
>
{buyType === "alive" ? (
<AliveFormSection
formik={formik}
cities={cityData}
provinces={provinceData}
isEdit={!!item}
item={item}
/>
) : (
<CarcassFormSection
formik={formik}
cities={cityData}
provinces={provinceData}
isEdit={!!item}
item={item}
/>
)}
<CommonFields
formikDriver={formikDriver}
driverPelak={driverPelak}
setDriverPelak={setDriverPelak}
/>
{buyType === "alive" && (
<FormControl>
<FormLabel id="inventory-date-radio-group">
تاریخ ورود به انبار
</FormLabel>
<RadioGroup
value={dayEnterInventory}
onChange={handleChangeDayEnterInventory}
row
aria-labelledby="inventory-date-radio-group"
name="inventory-date-radio-group"
>
<FormControlLabel
value="today"
control={<Radio />}
label="امروز"
/>
<FormControlLabel
value="tomorrow"
control={<Radio />}
label="فردا"
/>
</RadioGroup>
</FormControl>
)}
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"تصویر بار"}
/>
{formik.touched.bar_image && Boolean(formik.errors.bar_image) && (
<Typography color="error">ثبت تصویر بار الزامی است</Typography>
)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
disabled={!formik.isValid}
>
ثبت
</Button>
</form>
</Grid>
) : (
<Grid container direction="column" justifyContent="center" gap={2}>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="killer-select-automatic"
options={killersData.map((i) => ({
data: i.key,
label: `${i.name} (${i?.phone})`,
}))}
onChange={(event, value) => setKillerKey(value?.data)}
renderInput={(params) => (
<TextField
fullWidth
{...params}
label="انتخاب کشتارکن (اختیاری)"
/>
)}
/>
<TextField
id="quantity"
name="quantity"
disabled={item?.registerType === "automatic"}
label="حجم زنده"
InputProps={{
endAdornment: (
<InputAdornment position="end">قطعه</InputAdornment>
),
}}
value={formik.values.quantity}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.quantity && Boolean(formik.errors.quantity)}
helperText={formik.touched.quantity && formik.errors.quantity}
/>
<TextField
id="live_weight"
name="live_weight"
disabled={item?.registerType === "automatic"}
label="وزن بار زنده"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
value={formik.values.live_weight}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.live_weight && Boolean(formik.errors.live_weight)
}
helperText={formik.touched.live_weight && formik.errors.live_weight}
/>
<Button
onClick={formik.handleSubmit}
type="button"
fullWidth
variant="contained"
color="primary"
disabled={!formik.isValid}
>
ثبت
</Button>
</Grid>
)}
</Grid>
);
};