SupportTicket UI and API integration Updated

This commit is contained in:
Sibunnayak 2024-03-20 10:53:09 +05:30
parent de3300af02
commit ee638fb666
7 changed files with 1437 additions and 0 deletions

View File

@ -22,6 +22,7 @@ import {
cilTennisBall,
cilText,
cilUser,
cilAlarm,
} from "@coreui/icons";
import { CNavGroup, CNavItem, CNavTitle, CTabContent } from "@coreui/react";
@ -229,6 +230,25 @@ const _nav = [
],
},
//Affiliate end
{
component: CNavGroup,
name: "Customer Service",
icon: <CIcon icon={cilClipboard} customClassName="nav-icon" />,
items: [
{
component: CNavItem,
name: "Customer Support",
icon: <CIcon icon={cilAlarm} customClassName="nav-icon" />,
to: "/support/request",
},
{
component: CNavItem,
name: "Contact Requests",
icon: <CIcon icon={cilUser} customClassName="nav-icon" />,
to: "/contact/request",
},
],
},
];
export default _nav;

View File

@ -110,6 +110,10 @@ import EditCoupon from "./views/Affiliate/EditCoupon";
import PayAffiliate from "./views/Affiliate/PayAffiliate";
import AffiliateHistory from "./views/Affiliate/AffiliateHistory";
import CouponHistory from "./views/Affiliate/CouponHistory";
import SupportRequest from "./views/CustomerSupport/SupportRequest";
import SupportReply from "./views/CustomerSupport/SupportReply";
import SupportRequestClosed from "./views/CustomerSupport/SupportRequestClosed";
import CloseRequestView from "./views/CustomerSupport/CloseRequestView";
const routes = [
{ path: "/", exact: true, name: "Home" },
@ -238,7 +242,28 @@ const routes = [
name: "AddContact Request",
element: AddContactRequest,
},
//Support Requests
{
path: "/support/request",
name: "CustomerSupport Requests",
element: SupportRequest,
},
{
path: "/support/request/closed",
name: "Closed CustomerSupport Requests",
element: SupportRequestClosed,
},
{
path: "/support/request/closed/:ticketID",
name: "Closed CustomerSupport Request view",
element: CloseRequestView,
},
{
path: "/support/request/reply/:ticketID",
name: "CustomerSupport Reply",
element: SupportReply,
},
// Content ---- >
{

View File

@ -0,0 +1,207 @@
import React, { useEffect, useState } from "react";
// import Button from "@material-ui/core/Button";
import { Link, useNavigate } from "react-router-dom";
import swal from "sweetalert";
import axios from "axios";
import { isAutheticated } from "src/auth";
import { Button, ImageList, InputLabel, ImageListItem } from "@mui/material";
import MessageList from "./MessageList";
import { useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
const CloseRequestView = () => {
// Assuming you have the following ticket details
const { ticketID } = useParams();
const token = isAutheticated();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
// error****************************please check
const [ticketDetails, setticketDetails] = useState(null);
// const dispatch = useDispatch();
// const tickets = useSelector((state) => state.ticket.ticket);
// useEffect(() => {
// dispatch(getTicketItem());
// }, [dispatch]);
// console.log(tickets);
// geting data from backend************
// const dispatch = useDispatch();
// const tickets = useSelector((state) => state.ticket.ticket);
// useEffect(() => {
// dispatch(getTicketItem());
// }, [dispatch]);
// console.log(tickets);
// useEffect(() => {
// const foundTicket = tickets.find((ticket) => ticket.ticketId === ticketID);
// // console.log(foundTicket);
// setticketDetails(foundTicket);
// }, [ticketID]);
// console.log(ticketID);
// *****************
// geting data from backend************
const getSupportTicketDetails = async () => {
// console.log(ticketID);
axios
.get(`/api/support/getOne/${ticketID}`, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
// console.log(res?.data);
// setticketDetails(res.data?.support);
// setTicket(res.data?.support);
console.log(res.data?.support);
getuserName(res.data?.support);
// setLoading(false);
})
.catch((error) => {
swal({
title: error,
text: "please login to access the resource or refresh the page ",
icon: "error",
button: "Retry",
dangerMode: true,
});
// setLoading(false);
});
};
// ************************************
const getuserName = async (ticket) => {
try {
const userId = ticket.addedBy;
let resp = await axios.get(`/api/v1/admin/user/${userId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(resp?.data?.user?.name);
const userName = resp?.data?.user?.name;
const userMailId = resp?.data?.user?.email;
// console.log(userName);
// Update support ticket with user name
const ticketWithUserName = {
...ticket,
userName: userName,
userMailId: userMailId,
};
setticketDetails(ticketWithUserName);
} catch (error) {
// Handle errors
}
};
React.useEffect(() => {
getSupportTicketDetails();
}, []);
// *****************
return (
<div className="container">
<div className="row">
<div className="col-12">
<div
className="
page-title-box
d-flex
align-items-center
justify-content-between
"
>
<div style={{ fontSize: "22px" }} className="fw-bold">
View Request
</div>
<div style={{ display: "flex", gap: "1rem" }}>
<h4 className="mb-0"></h4>
</div>
<div className="page-title-right">
<Button
variant="outlined"
color="error"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
}}
onClick={() => {
navigate("/support/request/closed", { replace: true });
}}
>
Back
</Button>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12 my-1">
<div className="card h-100">
<div className="card-body px-5">
<div className="mb-3">
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Ticket ID: {ticketDetails?.ticketId}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User ID: {ticketDetails?.addedBy}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User Name: {ticketDetails?.userName}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User MailId: {ticketDetails?.userMailId}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Date and Time: {ticketDetails?.createdOn}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Subject: {ticketDetails?.subject}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Description:{" "}
{ticketDetails?.description
? ticketDetails?.description
: "No Description"}
</InputLabel>
</div>
<div className="mb-3">
{ticketDetails?.image?.length > 0 && (
<div>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Image:
</InputLabel>
<ImageList sx={{ width: 500 }} cols={3} rowHeight={164}>
{ticketDetails?.image?.map((item) => (
<ImageListItem key={item.public_id}>
<img
srcSet={`${item.url}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
src={`${item.url}?w=164&h=164&fit=crop&auto=format`}
alt={item.title || "No image"}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</div>
)}
</div>
<div className="mb-3">
{ticketDetails && (
<MessageList messages={ticketDetails?.message} />
)}
</div>
{/* *************** */}
</div>
</div>
</div>
</div>
</div>
);
};
export default CloseRequestView;

View File

@ -0,0 +1,32 @@
import React from 'react';
import { Paper } from '@material-ui/core';
const MessageList = ({ messages }) => {
return (
<>
{messages.map((msg, index) => (
<div key={index} style={{ marginBottom: '10px' }}>
<Paper
elevation={3}
style={{
padding: '10px',
backgroundColor: msg.user === 'user' ? 'white' : 'lightgray',
}}
>
<div style={{ fontWeight: 'bold', marginBottom: '5px' }}>
{msg.user === 'user' ? 'User' : 'Admin'}
</div>
<div style={{ fontWeight: 'bold', marginBottom: '5px' }}>
{msg.replyDate}
</div>
<div>{msg.message}</div>
</Paper>
</div>
))}
</>
);
};
export default MessageList;

View File

@ -0,0 +1,346 @@
import React, { useEffect, useState } from "react";
// import Button from "@material-ui/core/Button";
import { Link, useNavigate, useParams } from "react-router-dom";
import swal from "sweetalert";
import axios from "axios";
import { isAutheticated } from "src/auth";
import { Button, ImageList, InputLabel, ImageListItem } from "@mui/material";
import MessageList from "./MessageList";
import { useDispatch, useSelector } from "react-redux";
import toast from "react-hot-toast";
const SupportReply = () => {
const token = isAutheticated();
const navigate = useNavigate();
const [data, setData] = useState({
user: "admin",
replyDate: "",
message: "",
});
const [loading, setLoading] = useState(false);
const handleChange = (e) => {
setData((prev) => ({ ...prev, [e.target.id]: e.target.value }));
};
const options = {
weekday: "short", // Abbreviated weekday (e.g., "Tue")
day: "numeric", // Numeric day (e.g., "5")
month: "short", // Abbreviated month (e.g., "Mar")
year: "numeric", // Numeric year (e.g., "2024")
hour: "numeric", // Numeric hour (e.g., "10")
minute: "2-digit", // Two-digit minute (e.g., "27")
hour12: true, // Use 12-hour clock (true/false)
};
const { ticketID } = useParams();
const [ticketDetails, setticketDetails] = useState(null);
const [fetchData, setFetchData] = useState(false);
const getSupportTicketDetails = async () => {
// console.log(ticketID);
axios
.get(`/api/support/getOne/${ticketID}`, {
headers: {
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
// console.log(res?.data);
// setticketDetails(res.data?.support);
// setTicket(res.data?.support);
console.log(res.data?.support);
getuserName(res.data?.support);
// setLoading(false);
})
.catch((error) => {
swal({
title: error,
text: "please login to access the resource or refresh the page ",
icon: "error",
button: "Retry",
dangerMode: true,
});
// setLoading(false);
});
};
// ************************************
// updating message in backend************
const handleSubmit = () => {
if (data.message.trim() === "") {
swal({
title: "Warning",
text: "Fill all mandatory fields",
icon: "error",
button: "Close",
dangerMode: true,
});
return;
}
setLoading(true);
const formData = {
message: data.message,
replyDate: new Date().toLocaleString("en-US", options),
user: "admin",
};
axios
.patch(
`/api/support/update/${ticketID}`,
{
message: formData,
},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
)
.then((res) => {
toast.success("Message Sent successfully!");
setLoading(false);
setData({ message: "" });
setFetchData(true);
navigate("/support/request", { replace: true });
})
.catch((error) => {
swal({
title: "Error",
text: "Something went wrong",
icon: "error",
button: "Close",
});
});
};
// ************************************
const getuserName = async (ticket) => {
try {
const userId = ticket.addedBy;
let resp = await axios.get(`/api/v1/admin/user/${userId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(resp?.data?.user?.name);
const userName = resp?.data?.user?.name;
const userMailId = resp?.data?.user?.email;
// console.log(userName);
// Update support ticket with user name
const ticketWithUserName = {
...ticket,
userName: userName,
userMailId: userMailId,
};
setticketDetails(ticketWithUserName);
} catch (error) {
// Handle errors
}
};
React.useEffect(() => {
getSupportTicketDetails();
setFetchData(false);
}, [fetchData]);
// ************************************
return (
<div className="container">
<div className="row">
<div className="col-12">
<div
className="
page-title-box
d-flex
align-items-center
justify-content-between
"
>
<div style={{ fontSize: "22px" }} className="fw-bold">
Support Chat
</div>
<div style={{ display: "flex", gap: "1rem" }}>
<h4 className="mb-0"></h4>
</div>
<div className="page-title-right">
{/* <Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => handleSubmit()}
disabled={loading}
>
{loading ? "Loading" : "Submit"}
</Button> */}
{/* <Button
variant="outlined"
color="error"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
}}
onClick={() => {
swal({
title: "Warning",
text: "Are you sure you want to go back?",
icon: "error",
buttons: ["No", "Yes"], // Specify the buttons array
dangerMode: true,
}).then((value) => {
if (value) {
navigate("/support/request", { replace: true });
} else {
return;
}
});
}}
>
Back
</Button> */}
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12 my-1">
<div className="card h-100">
<div className="card-body px-5">
<div className="mb-3">
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Ticket ID: {ticketDetails?.ticketId}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User ID: {ticketDetails?.addedBy}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User Name: {ticketDetails?.userName}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
User MailId: {ticketDetails?.userMailId}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Date and Time: {ticketDetails?.createdOn}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Subject: {ticketDetails?.subject}
</InputLabel>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Description:{" "}
{ticketDetails?.description
? ticketDetails?.description
: "No Description"}
</InputLabel>
</div>
<div className="mb-3">
{ticketDetails?.image?.length > 0 && (
<div>
<InputLabel htmlFor="name" sx={{ mt: 1, mb: 2 }}>
Image:
</InputLabel>
<ImageList sx={{ width: 500 }} cols={3} rowHeight={164}>
{ticketDetails?.image?.map((item) => (
<ImageListItem key={item.public_id}>
<img
srcSet={`${item.url}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
src={`${item.url}?w=164&h=164&fit=crop&auto=format`}
alt={item.title || "No image"}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</div>
)}
</div>
<div className="mb-3">
{ticketDetails && (
<MessageList messages={ticketDetails?.message} />
)}
</div>
<div className="mb-3">
<label htmlFor="title" className="form-label">
Message *
</label>
<textarea
type="text"
className="form-control"
id="message"
rows="10"
cols="100"
value={data.message}
placeholder="your message..."
maxLength="500"
onChange={(e) => handleChange(e)}
></textarea>
{data.message ? (
<>
<small className="charLeft mt-4 fst-italic">
{500 - data.message.length} characters left
</small>
</>
) : (
<></>
)}
</div>
<div className="mb-3">
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => handleSubmit()}
disabled={loading}
>
{loading ? "Loading" : "Submit"}
</Button>
{/* <Link to="/support/request"> */}
<Button
variant="outlined"
color="error"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
}}
onClick={() => {
swal({
title: "Warning",
text: "Are you sure you want to go back?",
icon: "error",
buttons: ["No", "Yes"], // Specify the buttons array
dangerMode: true,
}).then((value) => {
if (value) {
navigate("/support/request", { replace: true });
} else {
return;
}
});
}}
>
Back
</Button>
{/* </Link> */}
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default SupportReply;

View File

@ -0,0 +1,428 @@
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import Button from "@material-ui/core/Button";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { isAutheticated } from "src/auth";
import { useDispatch, useSelector } from "react-redux";
import { CircularProgress, TextField } from "@material-ui/core";
import { cilSearch } from "@coreui/icons";
import CIcon from "@coreui/icons-react";
import Fuse from "fuse.js";
const SupportRequest = () => {
const token = isAutheticated();
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [SupportRequestsData, setSupportRequestsData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [itemPerPage, setItemPerPage] = useState(10);
const [showData, setShowData] = useState(SupportRequestsData);
const [searchTerm, setSearchTerm] = useState("");
useEffect(() => {
setTimeout(() => {
if (searchTerm !== "") {
let searchedResult = [];
searchedResult = SupportRequestsData.filter((item) =>
item.ticketId.toString().includes(searchTerm)
);
setShowData(searchedResult);
}
else{
getSupportTicketsData();
}
}, 100);
}, [searchTerm]);
const handleShowEntries = (e) => {
setCurrentPage(1);
setItemPerPage(e.target.value);
};
const getSupportTicketsData = async () => {
axios
.get(`/api/support/getAll/`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
setSupportRequestsData(
res.data?.support.filter((ticket) => ticket.status === "Open")
);
// setLoading(false);
})
.catch((error) => {
swal({
title: error,
text: "please login to access the resource or refresh the page ",
icon: "error",
button: "Retry",
dangerMode: true,
});
setLoading(false);
});
};
React.useEffect(() => {
setLoading(true);
getSupportTicketsData();
}, [token]);
const statusupdate = (ticketId) => {
axios
.patch(
`/api/support/update/${ticketId}`,
{
status: "Close",
},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
)
.then((res) => {
swal({
title: "Success",
text: "Ticket Closed Successfully",
icon: "success",
button: "Close",
});
getSupportTicketsData();
})
.catch((error) => {
swal({
title: "Error",
text: "Something went wrong",
icon: "error",
button: "Close",
});
});
};
useEffect(() => {
// const loadData = () => {
const indexOfLastPost = currentPage * itemPerPage;
const indexOfFirstPost = indexOfLastPost - itemPerPage;
setShowData(SupportRequestsData.slice(indexOfFirstPost, indexOfLastPost));
setLoading(false);
// };
// loadData();
}, [currentPage, itemPerPage, SupportRequestsData]);
return (
<div className="main-content">
<div className="page-content">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<div
className="
page-title-box
d-flex
align-items-center
justify-content-between
"
>
<div style={{ fontSize: "22px" }} className="fw-bold">
Customer Support Requests
</div>
<div className="page-title-right"></div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div className="card">
<div
className="card-body
"
>
<div className="d-flex align-items-center">
<div className="row ml-0 mr-0 mb-10">
<div className="col-sm-12 col-md-12">
<div className="dataTables_length">
<label className="w-auto">
Show
<select
style={{ width: "50px" }}
name=""
onChange={(e) => handleShowEntries(e)}
className="select-w custom-select custom-select-sm form-control form-control-sm"
>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
entries
</label>
</div>
</div>
</div>
<div className="ml-2 mt-2">
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => {
navigate(`/support/request`, {
replace: true,
});
}}
>
Open Requests
</Button>
<Button
variant="outlined"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
}}
onClick={() => {
navigate(`/support/request/closed`, {
replace: true,
});
}}
>
Close Requests
</Button>
</div>
{/* ********* */}
<div className="ml-5 mt-2">
<TextField
type="text"
placeholder="Search by Ticket ID"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<CIcon
icon={cilSearch}
size="xl"
/>
</div>
{/* ********** */}
</div>
<div className="table-responsive table-shoot mt-3">
<table
className="table table-centered table-nowrap"
style={{ border: "1px solid" }}
>
<thead
className="thead-info"
style={{ background: "rgb(140, 213, 213)" }}
>
<tr>
<th className="text-start">Ticket ID</th>
<th className="text-start">Subject</th>
<th className="text-start">Created On</th>
<th className="text-start">Last Reply</th>
<th className="text-start">Status</th>
<th
className="text-start"
style={{ paddingLeft: "15px" }}
>
Action
</th>
</tr>
</thead>
<tbody>
{!loading && showData.length === 0 && (
<tr className="text-center">
<td colSpan="6">
<h5>No Open Tickets Available</h5>
</td>
</tr>
)}
{loading ? (
<tr>
<td className="text-center" colSpan="6">
{/* Loading... */}
<CircularProgress />
</td>
</tr>
) : (
showData.map((ticket, i) => {
return (
<tr key={i}>
<td className="text-start">{ticket.ticketId}</td>
<td className="text-start">{ticket.subject}</td>
<td className="text-start">{ticket.createdOn}</td>
<td className="text-start">{ticket.lastreply}</td>
<td className="text-start">{ticket.status}</td>
<td className="text-start">
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => {
navigate(
`/support/request/reply/${ticket.ticketId}`,
{
replace: true,
}
);
}}
>
Reply
</Button>
<Button
variant="outlined"
color="error"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
color: "red",
}}
onClick={() => {
swal({
title: "Warning",
text: "Are you sure you want to Close This Ticket?",
icon: "error",
buttons: ["No", "Yes"],
dangerMode: true,
}).then((value) => {
if (value) {
statusupdate(ticket.ticketId);
} else {
return;
}
});
}}
>
Close
</Button>
</td>
</tr>
);})
)}
</tbody>
</table>
</div>
<div className="row mt-20">
<div className="col-sm-12 col-md-6 mb-20">
<div
className="dataTables_info"
id="datatable_info"
role="status"
aria-live="polite"
>
Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "}
{Math.min(
currentPage * itemPerPage,
SupportRequestsData.length
)}{" "}
of {SupportRequestsData.length} entries
</div>
</div>
<div className="col-sm-12 col-md-6">
<div className="d-flex">
<ul className="pagination ms-auto">
<li
className={
currentPage === 1
? "paginate_button page-item previous disabled"
: "paginate_button page-item previous"
}
>
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => setCurrentPage((prev) => prev - 1)}
>
Previous
</span>
</li>
{!(currentPage - 1 < 1) && (
<li className="paginate_button page-item">
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={(e) =>
setCurrentPage((prev) => prev - 1)
}
>
{currentPage - 1}
</span>
</li>
)}
<li className="paginate_button page-item active">
<span
className="page-link"
style={{ cursor: "pointer" }}
>
{currentPage}
</span>
</li>
{!(
(currentPage + 1) * itemPerPage - itemPerPage >
SupportRequestsData.length - 1
) && (
<li className="paginate_button page-item ">
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => {
setCurrentPage((prev) => prev + 1);
}}
>
{currentPage + 1}
</span>
</li>
)}
<li
className={
!(
(currentPage + 1) * itemPerPage - itemPerPage >
SupportRequestsData.length - 1
)
? "paginate_button page-item next"
: "paginate_button page-item next disabled"
}
>
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => setCurrentPage((prev) => prev + 1)}
>
Next
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default SupportRequest;

View File

@ -0,0 +1,379 @@
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import Button from "@material-ui/core/Button";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { isAutheticated } from "src/auth";
import { useDispatch, useSelector } from "react-redux";
import { CircularProgress, TextField } from "@material-ui/core";
import { cilSearch } from "@coreui/icons";
import CIcon from "@coreui/icons-react";
import Fuse from "fuse.js";
const SupportRequestClosed = () => {
const token = isAutheticated();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(true);
const [status, setStatus] = useState("");
const [SupportRequestsData, setSupportRequestsData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [itemPerPage, setItemPerPage] = useState(10);
const [showData, setShowData] = useState(SupportRequestsData);
const [searchTerm, setSearchTerm] = useState("");
useEffect(() => {
setTimeout(() => {
if (searchTerm !== "") {
let searchedResult = [];
searchedResult = SupportRequestsData.filter((item) =>
item.ticketId.toString().includes(searchTerm)
);
setShowData(searchedResult);
} else {
getSupportTicketsData();
}
}, 100);
}, [searchTerm]);
const handleShowEntries = (e) => {
setCurrentPage(1);
setItemPerPage(e.target.value);
};
const [ticketsData, setticketsData] = React.useState([]);
const getSupportTicketsData = async () => {
axios
.get(`/api/support/getAll/`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
setSupportRequestsData(
res.data?.support.filter((ticket) => ticket.status === "Close")
);
setLoading(false);
})
.catch((error) => {
swal({
title: error,
text: "please login to access the resource or refresh the page ",
icon: "error",
button: "Retry",
dangerMode: true,
});
setLoading(false);
});
};
React.useEffect(() => {
setLoading(true);
getSupportTicketsData();
}, [token]);
// console.log(showData);
//********************************* */
useEffect(() => {
const loadData = () => {
const indexOfLastPost = currentPage * itemPerPage;
const indexOfFirstPost = indexOfLastPost - itemPerPage;
setShowData(SupportRequestsData.slice(indexOfFirstPost, indexOfLastPost));
};
loadData();
}, [currentPage, itemPerPage, SupportRequestsData]);
return (
<div className="main-content">
<div className="page-content">
<div className="container-fluid">
<div className="row">
<div className="col-12">
<div
className="
page-title-box
d-flex
align-items-center
justify-content-between
"
>
<div style={{ fontSize: "22px" }} className="fw-bold">
Closed Customer Support Requests
</div>
<div className="page-title-right"></div>
</div>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div className="card">
<div
className="card-body
"
>
<div className="d-flex align-items-center">
<div className="row ml-0 mr-0 mb-10">
<div className="col-sm-12 col-md-12">
<div className="dataTables_length">
<label className="w-auto">
Show
<select
style={{ width: "50px" }}
name=""
onChange={(e) => handleShowEntries(e)}
className="select-w custom-select custom-select-sm form-control form-control-sm"
>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
entries
</label>
</div>
</div>
</div>
<div className="ml-2 mt-2">
<Button
variant="contained"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
onClick={() => {
navigate(`/support/request`, {
replace: true,
});
}}
>
Open Requests
</Button>
<Button
variant="outlined"
color="error"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
color: "red",
}}
onClick={() => {
navigate(`/support/request/closed`, {
replace: true,
});
}}
>
Close Requests
</Button>
</div>
{/* ********* */}
<div className="ml-5 mt-2">
<TextField
type="text"
placeholder="Search by Ticket ID"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<CIcon
icon={cilSearch}
size="xl"
// onClick={() => handleSearch(searchTerm)}
/>
</div>
{/* ********** */}
</div>
<div className="table-responsive table-shoot mt-3">
<table
className="table table-centered table-nowrap"
style={{ border: "1px solid" }}
>
<thead
className="thead-info"
style={{ background: "rgb(140, 213, 213)" }}
>
<tr>
<th className="text-start">Ticket ID</th>
<th className="text-start">Subject</th>
<th className="text-start">Created On</th>
<th className="text-start">Last Reply</th>
<th className="text-start">Status</th>
<th
className="text-start"
style={{ paddingLeft: "15px" }}
>
Action
</th>
{/* <th className="text-start">Action</th> */}
</tr>
</thead>
<tbody>
{!loading && showData.length === 0 && (
<tr className="text-center">
<td colSpan="6">
<h5>No Close Tickets Available</h5>
</td>
</tr>
)}
{loading ? (
<tr>
<td className="text-center" colSpan="6">
{/* Loading... */}
<CircularProgress />
</td>
</tr>
) : (
showData.map((ticket, i) => {
return (
<tr key={i}>
<td className="text-start">
{ticket.ticketId}
</td>
<td className="text-start">{ticket.subject}</td>
<td className="text-start">
{ticket.createdOn}
</td>
<td className="text-start">
{ticket.lastreply}
</td>
<td className="text-start">{ticket.status}</td>
<td className="text-start">
<Link
to={`/support/request/closed/${ticket.ticketId}`}
>
<Button
variant="contained"
color="primary"
style={{
fontWeight: "bold",
marginBottom: "1rem",
textTransform: "capitalize",
marginRight: "5px",
}}
>
View
</Button>
</Link>
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
<div className="row mt-20">
<div className="col-sm-12 col-md-6 mb-20">
<div
className="dataTables_info"
id="datatable_info"
role="status"
aria-live="polite"
>
Showing {currentPage * itemPerPage - itemPerPage + 1} to{" "}
{Math.min(
currentPage * itemPerPage,
SupportRequestsData.length
)}{" "}
of {SupportRequestsData.length} entries
</div>
</div>
<div className="col-sm-12 col-md-6">
<div className="d-flex">
<ul className="pagination ms-auto">
<li
className={
currentPage === 1
? "paginate_button page-item previous disabled"
: "paginate_button page-item previous"
}
>
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => setCurrentPage((prev) => prev - 1)}
>
Previous
</span>
</li>
{!(currentPage - 1 < 1) && (
<li className="paginate_button page-item">
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={(e) =>
setCurrentPage((prev) => prev - 1)
}
>
{currentPage - 1}
</span>
</li>
)}
<li className="paginate_button page-item active">
<span
className="page-link"
style={{ cursor: "pointer" }}
>
{currentPage}
</span>
</li>
{!(
(currentPage + 1) * itemPerPage - itemPerPage >
SupportRequestsData.length - 1
) && (
<li className="paginate_button page-item ">
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => {
setCurrentPage((prev) => prev + 1);
}}
>
{currentPage + 1}
</span>
</li>
)}
<li
className={
!(
(currentPage + 1) * itemPerPage - itemPerPage >
SupportRequestsData.length - 1
)
? "paginate_button page-item next"
: "paginate_button page-item next disabled"
}
>
<span
className="page-link"
style={{ cursor: "pointer" }}
onClick={() => setCurrentPage((prev) => prev + 1)}
>
Next
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default SupportRequestClosed;