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,131 @@
import React, { useContext, useState } from "react";
import {
Button,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterEditDelegatesService } from "../../services/slaughter-get-delegates-service";
export const DelegatesLimitationForm = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasLimitation, setHasLimitation] = useState(item?.limitation || false);
const [governmentalValue, setGovernmentalValue] = useState(
item?.governmentalLimitationWeight || 0
);
const [freeValue, setFreeValue] = useState(item?.freeLimitationWeight || 0);
const handleSubmit = (e) => {
e.preventDefault();
const submitData = {
key: item?.key,
limitation: hasLimitation,
governmental_limitation_weight: hasLimitation
? Number(governmentalValue)
: 0,
free_limitation_weight: hasLimitation ? Number(freeValue) : 0,
};
dispatch(slaughterEditDelegatesService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
};
return (
<form onSubmit={handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid container item xs={12} alignItems="center" gap={1}>
<Typography variant="body2" color="text.secondary">
اطلاعات نماینده:
</Typography>
<Typography variant="h6" mb={0.75}>
{item?.firstName || item?.first_name}{" "}
{item?.lastName || item?.last_name}
</Typography>
</Grid>
<Grid item xs={12} mb={1}>
<FormControlLabel
control={
<Checkbox
checked={hasLimitation}
onChange={(e) => setHasLimitation(e.target.checked)}
color="primary"
/>
}
label="محدودیت فروش روزانه"
/>
</Grid>
{hasLimitation && (
<>
<Grid item xs={12}>
<TextField
label="حداکثر فروش دولتی (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={governmentalValue}
onChange={(e) => setGovernmentalValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="حداکثر فروش آزاد (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={freeValue}
onChange={(e) => setFreeValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
</>
)}
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={
hasLimitation && governmentalValue === 0 && freeValue === 0
}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,139 @@
import { IconButton, Popover, Typography, Button } from "@mui/material";
import React, { useState, useContext } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import ToggleOffIcon from "@mui/icons-material/ToggleOff";
import BlockIcon from "@mui/icons-material/Block";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterEditDelegatesService } from "../../services/slaughter-get-delegates-service";
import { DelegatesLimitationForm } from "./DelegatesLimitationForm";
export const DelegatesOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const handleToggleActive = () => {
handleClose();
dispatch(
slaughterEditDelegatesService({
key: item?.key,
active: !isActive,
})
).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",
});
if (updateTable) {
updateTable();
}
}
});
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div
style={{
padding: "10px",
display: "flex",
flexDirection: "column",
gap: "8px",
}}
>
<Button
color={isActive ? "error" : "success"}
size="small"
onClick={handleToggleActive}
startIcon={
isActive ? (
<ToggleOffIcon fontSize="small" />
) : (
<ToggleOnIcon fontSize="small" />
)
}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
{isActive ? "غیرفعال کردن" : "فعال کردن"}
</Typography>
</Button>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تنظیم محدودیت فروش",
content: (
<DelegatesLimitationForm
item={item}
updateTable={updateTable}
/>
),
size: 400,
})
);
}}
startIcon={<BlockIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
تنظیم محدودیت
</Typography>
</Button>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDelegatesService } from "../../services/slaughter-get-delegates-service";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DelegatesOperations } from "./DelegatesOperations";
export const KillHouseDelegatesTab = () => {
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDelegatesService({
type: "KillHouse",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const killhouseDisplay =
item?.killHouse?.name && item?.killHouse?.mobile
? `${item.killHouse.name} (${item.killHouse.mobile})`
: item?.killHouse?.name
? item.killHouse.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || item?.first_name || "-",
item?.lastName || item?.last_name || "-",
item?.mobile || "-",
item?.city || "-",
killhouseDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DelegatesOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDelegatesService({
type: "KillHouse",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="نمایندگان کشتارگاه‌ها"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"کشتارگاه",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,27 @@
import React, { useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs, Box } from "@mui/material";
import { StewardDelegatesTab } from "./StewardDelegatesTab";
import { KillHouseDelegatesTab } from "./KillHouseDelegatesTab";
export const SlaughterHouseDelegates = () => {
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Tabs value={value} onChange={handleChange}>
<Tab label="مباشرین" value={0} />
<Tab label="کشتارگاه‌ها" value={1} />
</Tabs>
<Box sx={{ width: "100%", mt: 2 }}>
{value === 0 && <StewardDelegatesTab />}
{value === 1 && <KillHouseDelegatesTab />}
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDelegatesService } from "../../services/slaughter-get-delegates-service";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DelegatesOperations } from "./DelegatesOperations";
export const StewardDelegatesTab = () => {
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDelegatesService({
type: "Steward",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const stewardDisplay =
item?.steward?.name && item?.steward?.user?.mobile
? `${item.steward.name} (${item.steward.user.mobile})`
: item?.steward?.name
? item.steward.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || item?.first_name || "-",
item?.lastName || item?.last_name || "-",
item?.mobile || "-",
item?.city || "-",
stewardDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DelegatesOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDelegatesService({
type: "Steward",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="نمایندگان مباشرین"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"مباشر",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};