diff --git a/frontend/src/App.js b/frontend/src/App.js
index cadef20..006f899 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -7,7 +7,8 @@ import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import NewTicket from "./pages/NewTicket";
-import Tickets from "./pages/Ticket";
+import Tickets from "./pages/Tickets";
+import Ticket from "./pages/Ticket";
function App() {
return (
@@ -25,6 +26,9 @@ function App() {
}>
} />
+ }>
+ } />
+
diff --git a/frontend/src/components/TicketItem.jsx b/frontend/src/components/TicketItem.jsx
new file mode 100644
index 0000000..4bdf932
--- /dev/null
+++ b/frontend/src/components/TicketItem.jsx
@@ -0,0 +1,15 @@
+import { Link } from "react-router-dom";
+
+function TicketItem({ ticket }) {
+ return (
+
+
{new Date(ticket.createdAt).toLocaleString("en-US")}
+
{ticket.product}
+
{ticket.status}
+
+ View
+
+
+ );
+}
+export default TicketItem;
diff --git a/frontend/src/features/tickets/ticketService.js b/frontend/src/features/tickets/ticketService.js
index 3df23cc..8063f4d 100644
--- a/frontend/src/features/tickets/ticketService.js
+++ b/frontend/src/features/tickets/ticketService.js
@@ -19,12 +19,39 @@ const getTickets = async (token) => {
Authorization: `Bearer ${token}`,
},
};
-
const response = await axios.get(API_URL, config);
return response.data;
};
-const ticketService = { createTicket, getTickets };
+const closeTicket = async (ticketId, token) => {
+ const config = {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ };
+
+ const response = await axios.put(
+ API_URL + ticketId,
+ { status: "closed" },
+ config
+ );
+
+ return response.data;
+};
+
+const getTicket = async (ticketId, token) => {
+ const config = {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ };
+
+ const response = await axios.get(API_URL + ticketId, config);
+
+ return response.data;
+};
+
+const ticketService = { getTicket, createTicket, getTickets, closeTicket };
export default ticketService;
diff --git a/frontend/src/features/tickets/ticketSlice.js b/frontend/src/features/tickets/ticketSlice.js
index 6fb2b2c..a3affd6 100644
--- a/frontend/src/features/tickets/ticketSlice.js
+++ b/frontend/src/features/tickets/ticketSlice.js
@@ -42,10 +42,49 @@ export const ticketSlice = createSlice({
state.isLoading = false;
state.isError = true;
state.message = action.payload;
+ })
+ .addCase(getTicket.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(getTicket.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.isSuccess = true;
+ state.ticket = action.payload;
+ })
+ .addCase(getTicket.rejected, (state, action) => {
+ state.isLoading = false;
+ state.isError = true;
+ state.message = action.payload;
+ })
+ .addCase(closeTicket.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.tickets.map((ticket) =>
+ ticket._id === action.payload._id
+ ? (ticket.status = "closed")
+ : ticket
+ );
});
},
});
+export const closeTicket = createAsyncThunk(
+ "tickets/close",
+ async (ticketId, thunkAPI) => {
+ try {
+ const token = thunkAPI.getState().auth.user.token;
+ return await ticketService.closeTicket(ticketId, token);
+ } catch (error) {
+ const message =
+ (error.response &&
+ error.response.data &&
+ error.response.data.message) ||
+ error.message ||
+ error.toString();
+
+ return thunkAPI.rejectWithValue(message);
+ }
+ }
+);
export const getTickets = createAsyncThunk(
"tickets/getAll",
async (_, thunkAPI) => {
@@ -69,7 +108,25 @@ export const createTicket = createAsyncThunk(
async (ticketData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
- return await ticketService(ticketData, token);
+ return await ticketService.createTicket(ticketData, token);
+ } catch (error) {
+ const message =
+ (error.response &&
+ error.response.data &&
+ error.response.data.message) ||
+ error.message ||
+ error.toString();
+
+ return thunkAPI.rejectWithValue(message);
+ }
+ }
+);
+export const getTicket = createAsyncThunk(
+ "tickets/get",
+ async (ticketId, thunkAPI) => {
+ try {
+ const token = thunkAPI.getState().auth.user.token;
+ return await ticketService.getTicket(ticketId, token);
} catch (error) {
const message =
(error.response &&
diff --git a/frontend/src/pages/NewTicket.jsx b/frontend/src/pages/NewTicket.jsx
index 1cc5613..3e25d5c 100644
--- a/frontend/src/pages/NewTicket.jsx
+++ b/frontend/src/pages/NewTicket.jsx
@@ -9,7 +9,7 @@ import { BackButton } from "../components/BackButton";
function NewTicket() {
const { user } = useSelector((state) => state.auth);
const { isLoading, isError, isSuccess, message } = useSelector(
- (state) => state.ticket
+ (state) => state.tickets
);
const [name, setName] = useState(user.name);
@@ -20,26 +20,30 @@ function NewTicket() {
const dispatch = useDispatch();
const navigate = useNavigate();
- useEffect(()=>{
- if(isError) toast.error(message)
+ useEffect(() => {
+ if (isError) toast.error(message);
- if(isSuccess){
- dispatch(reset())
- navigate('/tickets')
+ if (isSuccess) {
+ dispatch(reset());
+ navigate("/tickets");
}
- dispatch(reset())
- },[dispatch,isError,isSuccess,navigate,message])
+ dispatch(reset());
+ }, [dispatch, isError, isSuccess, navigate, message]);
const onSubmit = (e) => {
e.preventDefault();
- dispatch(createTicket({product,description}))
+ if (product === "") {
+ dispatch(createTicket({ product:'iPhone', description }));
+ } else {
+ dispatch(createTicket({ product, description }));
+ }
};
- if(isLoading) return
+ if (isLoading) return ;
return (
<>
-
+
Create New Ticket
Please fill out the form below
diff --git a/frontend/src/pages/Ticket.jsx b/frontend/src/pages/Ticket.jsx
index f8d9091..44e16c0 100644
--- a/frontend/src/pages/Ticket.jsx
+++ b/frontend/src/pages/Ticket.jsx
@@ -1,28 +1,56 @@
import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
-import { getTickets, reset } from "../features/tickets/ticketSlice";
+import { closeTicket, getTicket, reset } from "../features/tickets/ticketSlice";
+import { useNavigate, useParams } from "react-router-dom";
import Spinner from "../components/Spinner";
import { BackButton } from "../components/BackButton";
+import { toast } from "react-toastify";
-function Tickets() {
- const { tickets, isLoading, isSuccess } = useSelector(
+function Ticket() {
+ const { ticket, isLoading, isSuccess, isError, message } = useSelector(
(state) => state.tickets
);
+ const { ticketId } = useParams();
const dispatch = useDispatch();
- useEffect(() => {
- return () => {
- if (isSuccess) {
- dispatch(reset());
- }
- };
- }, [dispatch, isSuccess]);
- useEffect(() => {
- dispatch(getTickets());
- }, [dispatch]);
+ const navigate = useNavigate();
- if (isLoading) {
- return ;
+ const onTicketClose =()=>{
+ dispatch(closeTicket(ticketId))
+ toast.success('Ticket closed')
+ navigate('/tickets')
}
- return Head
;
+ useEffect(() => {
+ if (isError) toast.error(message);
+
+ dispatch(getTicket(ticketId));
+ }, [isError, message, ticketId]);
+ if (isLoading) return ;
+ if (isError) return Something went wrong
;
+
+ return (
+
+
+
+
+ Ticket ID: {ticket._id}
+
+ {ticket.status}
+
+
+
+ Date Submitted: {new Date(ticket.createdAt).toLocaleString("en-US")}
+
+ Product: {ticket.product}
+
+
+
Description of Issue
+
{ticket.description}
+
+
+ {ticket.status !== "closed" && (
+
+ )}
+
+ );
}
-export default Tickets;
+export default Ticket;
diff --git a/frontend/src/pages/Tickets.jsx b/frontend/src/pages/Tickets.jsx
new file mode 100644
index 0000000..53fd561
--- /dev/null
+++ b/frontend/src/pages/Tickets.jsx
@@ -0,0 +1,43 @@
+import { useEffect } from "react";
+import { useSelector, useDispatch } from "react-redux";
+import { getTickets, reset } from "../features/tickets/ticketSlice";
+import Spinner from "../components/Spinner";
+import { BackButton } from "../components/BackButton";
+import TicketItem from "../components/TicketItem";
+
+function Tickets() {
+ const { tickets, isLoading, isSuccess } = useSelector(
+ (state) => state.tickets
+ );
+ const dispatch = useDispatch();
+ useEffect(() => {
+ return () => {
+ if (isSuccess) {
+ dispatch(reset());
+ }
+ };
+ }, [dispatch, isSuccess]);
+ useEffect(() => {
+ dispatch(getTickets());
+ }, [dispatch]);
+
+ if (isLoading) {
+ return ;
+ }
+ return <>
+
+ Tickets
+
+
+
Date
+
Product
+
Status
+
+
+ {tickets.map((ticket)=>(
+
+ ))}
+
+ >
+}
+export default Tickets;