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

599
src/pages/TicketList.js Normal file
View File

@@ -0,0 +1,599 @@
import React, { useContext, useEffect, useState } from "react";
import {
Box,
Button,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch } from "react-redux";
import axios from "axios";
import { Grid } from "../components/grid/Grid";
import {
CLOSE_MODAL,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../lib/redux/slices/appSlice";
import ResponsiveTable from "../components/responsive-table/ResponsiveTable";
import { getFaUserRole } from "../utils/getFaUserRole";
import { useNavigate } from "react-router-dom";
import persianDate from "persian-date";
import useUserProfile from "../features/authentication/hooks/useUserProfile";
import CommentsDisabledIcon from "@mui/icons-material/CommentsDisabled";
import { AppContext } from "../contexts/AppContext";
import { CloseTicketService } from "../features/ticket/services/create-ticket";
import personalTicketImage from "../../src/assets/images/Ticket1.png";
import publicTicketImage from "../../src/assets/images/ticket2.png";
import ClosedTicketImage from "../../src/assets/images/ClosedTicketImage.png";
const TicketList = () => {
const navigate = useNavigate();
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [role] = useUserProfile();
const isAdmin = () => {
if (
role.includes("CityOperator") ||
role.includes("ProvinceOperator") ||
role.includes("AdminX") ||
role.includes("Supporter") ||
role.includes("SuperAdmin")
) {
return true;
} else {
return false;
}
};
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 [openNotif] = useContext(AppContext);
const [value, setValue] = useState("0");
const [selectedTicket, setSelectedTicket] = useState("0");
const [unreadMessages, setUnredMessages] = useState([]);
// const [ticketCounts, setTicketCounts] = useState({
// personal: 0,
// public: 0,
// closed: 0,
// });
// const handleChange = (event, newValue) => {
// setValue(newValue);
// };
const fetchApiData = async (page) => {
let response;
try {
dispatch(LOADING_START());
let query = `ticket/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}`;
if (value === "0") query += `&type=single&status=open`;
else if (value === "1") query += `&type=public&status=open`;
else if (value === "2") query += `&status=closed`;
response = await axios.get(query);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
} catch {
dispatch(LOADING_END());
}
};
const dispatch = useDispatch();
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
fetchApiData(1);
}, [dispatch, perPage, value]);
useEffect(() => {
let unread = [];
const d = data?.map((item, i) => {
if (item?.unreadMessage) {
unread[i] = true;
} else {
unread[i] = false;
}
return [
page === 1 ? i + 1 : i + (perPage * page) / 2 + 1,
item?.ticketId,
item.typeTicket === "single" ? "شخصی" : "همگانی",
`${item?.role ? getFaUserRole(item?.role) : ""} (${
item?.user?.fullname
})`,
item?.title,
`${
item?.status === "open"
? `باز${item?.readOnly === true ? " (فقط خواندنی)" : ""}`
: item?.status === "answered"
? "پاسخ داده شده"
: "بسته"
}`,
item.toUser.length
? item?.toUser?.map(
(option, index) =>
`${option?.fullname} ${
index + 1 !== item?.toUser?.length ? " - " : ""
}`
)
: item?.toRole?.map(
(option, index) =>
`${getFaUserRole(option.name)} ${
index + 1 !== item?.toRole?.length ? " - " : ""
}`
),
`${new persianDate(new Date(item?.createDate)).format(
"dddd DD MMMM"
)} (${new Date(item?.createDate).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
})})`,
<Grid
xs={12}
container
justifyContent="center"
alignItems="center"
gap={1}
key={i}
>
<Button
onClick={() => {
navigate(`${item?.ticketId}/false`);
}}
>
مشاهده
</Button>
{isAdmin() && (
<Tooltip placement="left" title="بستن تیکت">
<IconButton
disabled={item?.status === "closed"}
color="error"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "از بستن تیکت اطمینان دارید؟",
content: (
<Grid
container
justifyContent="center"
alignItems="center"
>
<Typography color="error" variant="caption">
در صورت بستن تیکت امکان باز کردن مجدد آن وجود ندارد!
</Typography>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
mt={2}
gap={2}
>
<Button
variant="contained"
onClick={() => {
dispatch(
CloseTicketService({
ticket: item?.ticketId,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
>
تایید
</Button>
<Button
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
<CommentsDisabledIcon />
</IconButton>
</Tooltip>
)}
</Grid>,
];
});
setTableData(d);
setUnredMessages(unread);
}, [data]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
let query = `ticket/?search=filter&value=${textValue}&page=1&page_size=${perPage}`;
if (value === "0") query += `&type=single`;
else if (value === "1") query += `&type=public`;
else if (value === "2") query += `&status=closed`;
const response = await axios.get(query);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const handleTicketTypeSelect = (type) => {
setValue(type);
setSelectedTicket(type);
};
return (
<>
<Box display={"flex"} justifyContent="center">
<Grid
container
direction="column"
justifyContent="center"
xs={12}
sm={12}
md={10}
lg={10}
>
<Grid container direction="column" width="100%" gap={2} mb={2}>
<Box
display="flex"
justifyContent="flex-start"
width="100%"
sx={{
justifyContent: "start",
borderBottom: "1px solid #e2dbdb",
width: "100%",
pb: 1,
color: "#2D5FFF",
}}
>
<Typography sx={{ font_size: "16px" }}>تیکت های من </Typography>
</Box>
<Box display="flex" justifyContent="flex-start" width="100%">
<Button variant="contained" onClick={() => navigate(`noid/true`)}>
ثبت تیکت جدید
</Button>
</Box>
</Grid>
<Grid container xs={12} justifyContent="center" alignItems="center">
{/* <Tabs
value={value}
onChange={handleChange}
aria-label="secondary tabs example"
>
<Tab value="0" label="شخصی" />
<Tab value="1" label="همگانی" />
</Tabs> */}
</Grid>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
mt={2}
>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={4}
>
<Grid
container
justifyContent="center"
direction="column"
style={{
cursor: "pointer",
}}
onClick={() => handleTicketTypeSelect("0")}
gap={1}
>
<Grid item>
<img
src={personalTicketImage}
alt="Personal Tickets"
width={70}
style={{
filter:
selectedTicket === "0"
? "saturate(100%) invert(22%) sepia(85%) saturate(746%) hue-rotate(200deg)"
: "none",
}}
/>
</Grid>
<Grid item>
<Typography variant="caption">تیکت های شخصی</Typography>
</Grid>
</Grid>
<Grid
container
justifyContent="center"
direction="column"
style={{
cursor: "pointer",
}}
onClick={() => handleTicketTypeSelect("1")}
gap={1}
>
<Grid item>
<img
src={publicTicketImage}
style={{
filter:
selectedTicket === "1"
? "saturate(100%) invert(22%) sepia(85%) saturate(746%) hue-rotate(200deg)"
: "none",
}}
alt="Public Tickets"
width={70}
/>
</Grid>
<Grid item>
<Typography variant="caption">تیکت های همگانی</Typography>
</Grid>
</Grid>
<Grid
container
justifyContent="center"
direction="column"
style={{ cursor: "pointer" }}
onClick={() => handleTicketTypeSelect("2")}
gap={1}
>
<Grid item>
<img
src={ClosedTicketImage}
alt="Closed Tickets"
width={70}
style={{
filter:
selectedTicket === "2"
? "saturate(100%) invert(22%) sepia(85%) saturate(746%) hue-rotate(200deg)"
: "none",
}}
/>
</Grid>
<Grid item>
<Typography variant="caption">تیکتهای بایگانی</Typography>
</Grid>
</Grid>
{/* <Grid>
<img src={closedTicketImage} alt="Closed Tickets" width={100} />
<Typography variant="caption">
تیکت های بسته شده: {ticketCounts.closed}
</Typography>
</Grid> */}
</Grid>
</Grid>
{value === "0" && (
<Grid container xs={12}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
mt={4}
>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button type="submit">جستجو</Button>
</form>
</Grid>
<Grid container xs={12}>
<ResponsiveTable
title="تیکت های شخصی"
columns={[
"ردیف",
"شماره تیکت",
"نوع تیکت",
"سازنده تیکت",
"عنوان",
"وضعیت",
"ارسال شده به",
"زمان ارسال",
"عملیات",
]}
allColors={{ color: "#142B73", text: "white" }}
customColors={[
{ name: "نوع تیکت", color: "#00B88A", text: "white" },
]}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
activeRows={unreadMessages}
/>
</Grid>
</Grid>
)}
{value === "1" && (
<Grid container xs={12}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
mt={4}
>
<Button
variant="contained"
onClick={() => {
navigate(`noid/true`);
}}
>
ایجاد تیکت
</Button>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button type="submit">جستجو</Button>
</form>
</Grid>
<Grid container xs={12}>
<ResponsiveTable
title="تیکت های همگانی"
columns={[
"ردیف",
"شماره تیکت",
"نوع تیکت",
"سازنده تیکت",
"عنوان",
"وضعیت",
"ارسال شده به",
"زمان ارسال",
"عملیات",
]}
allColors={{ color: "#142B73", text: "white" }}
customColors={[
{ name: "نوع تیکت", color: "#00B88A", text: "white" },
]}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
activeRows={unreadMessages}
/>
</Grid>
</Grid>
)}
{value === "2" && (
<Grid container xs={12}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
mt={4}
>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button type="submit">جستجو</Button>
</form>
</Grid>
<Grid container xs={12}>
<ResponsiveTable
title="تیکت‌های بایگانی‌شده"
columns={[
"ردیف",
"شماره تیکت",
"نوع تیکت",
"سازنده تیکت",
"عنوان",
"وضعیت",
"ارسال شده به",
"زمان ارسال",
"عملیات",
]}
allColors={{ color: "#142B73", text: "white" }}
customColors={[
{ name: "نوع تیکت", color: "#00B88A", text: "white" },
]}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
activeRows={unreadMessages}
/>
</Grid>
</Grid>
)}
</Grid>
</Box>
</>
);
};
export default TicketList;