added notes to the frontend
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
const asyncHandler = require("express-async-handler");
|
||||
|
||||
const User = require("../models/userModel");
|
||||
const Note = require("../models/noteModel");
|
||||
const Ticket = require("../models/ticketModel");
|
||||
|
||||
// @desc Get notes for a ticket
|
||||
// @route GET /api/tickets/:ticketId/notes
|
||||
// @access Private
|
||||
const getNotes = asyncHandler(async (req, res) => {
|
||||
const user = await User.findById(req.user.id);
|
||||
|
||||
if (!user) {
|
||||
res.status(401);
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
const ticket = await Ticket.findById(req.params.ticketId)
|
||||
if (!ticket) {
|
||||
res.status(404);
|
||||
throw new Error("Ticket not found");
|
||||
}
|
||||
if (ticket.user.toString() !== req.user.id) {
|
||||
res.status(401);
|
||||
throw new Error("Not authorized");
|
||||
}
|
||||
|
||||
const notes = await Note.find({ ticket: req.params.ticketId });
|
||||
|
||||
res.status(201).json(notes);
|
||||
});
|
||||
// @desc Creata a note
|
||||
// @route POST /api/tickets/:ticketId/notes
|
||||
// @access Private
|
||||
const addNote = asyncHandler(async (req, res) => {
|
||||
const user = await User.findById(req.user.id);
|
||||
|
||||
if (!user) {
|
||||
res.status(401);
|
||||
throw new Error("User not found");
|
||||
}
|
||||
const ticket = await Ticket.findById(req.params.ticketId);
|
||||
if (!ticket) {
|
||||
res.status(404);
|
||||
throw new Error("Ticket not found");
|
||||
}
|
||||
if (ticket.user.toString() !== req.user.id) {
|
||||
res.status(401);
|
||||
throw new Error("Not authorized");
|
||||
}
|
||||
const note = await Note.create({
|
||||
text: req.body.text,
|
||||
ticket: req.params.ticketId,
|
||||
isStaff:false,
|
||||
user: req.user.id
|
||||
});
|
||||
res.status(201).json(note);
|
||||
});
|
||||
|
||||
module.exports = {addNote, getNotes };
|
||||
@@ -0,0 +1,32 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const noteSchema = mongoose.Schema(
|
||||
{
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: "User",
|
||||
},
|
||||
ticket: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
required: true,
|
||||
ref: "Ticket",
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: [true, "Please add some text"],
|
||||
},
|
||||
isStaff: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
staffId: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = mongoose.model("Note", noteSchema);
|
||||
@@ -0,0 +1,8 @@
|
||||
const express = require("express");
|
||||
const router = express.Router({ mergeParams: true });
|
||||
const protect = require("../middleware/authMiddleware");
|
||||
const { getNotes, addNote } = require("../controllers/noteController");
|
||||
|
||||
router.route("/").get(protect, getNotes).post(protect, addNote);
|
||||
|
||||
module.exports = router;
|
||||
@@ -10,6 +10,8 @@ const {
|
||||
|
||||
const protect = require("../middleware/authMiddleware");
|
||||
|
||||
const noteRouter = require('./noteRoutes')
|
||||
router.use('/:ticketId/notes',noteRouter)
|
||||
|
||||
router.route("/").get(protect, getTickets).post(protect, createTicket);
|
||||
router
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import authSlice from "../features/auth/authSlice";
|
||||
import ticketSlice from "../features/tickets/ticketSlice";
|
||||
import authReducer from "../features/auth/authSlice";
|
||||
import ticketReducer from "../features/tickets/ticketSlice";
|
||||
import noteReducer from "../features/notes/noteSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
auth: authSlice,
|
||||
tickets: ticketSlice,
|
||||
auth: authReducer,
|
||||
tickets: ticketReducer,
|
||||
notes: noteReducer,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
function NoteItem({ note }) {
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="note"
|
||||
style={{
|
||||
backgroundColor: note.isStaff ? "rgba(0,0,0,0.7)" : "#fff",
|
||||
color: note.isStaff ? "#fff" : "#000",
|
||||
}}
|
||||
>
|
||||
<h4>Note from {note.isStaff ? <span>Staff</span>:<span>{user.name}</span>}</h4>
|
||||
<p>{note.text}</p>
|
||||
<div className="note=date">
|
||||
{new Date(note.createdAt).toLocaleString('en-US')}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default NoteItem
|
||||
@@ -0,0 +1,35 @@
|
||||
import axios from "axios";
|
||||
|
||||
const API_URL = "/api/tickets/";
|
||||
|
||||
const createNote = async (noteText, ticketId, token) => {
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
};
|
||||
const response = await axios.post(
|
||||
API_URL + ticketId + "/notes",
|
||||
{ text: noteText },
|
||||
config
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
const getNotes = async (ticketId, token) => {
|
||||
const config = {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await axios.get(API_URL + ticketId + "/notes", config);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
const noteService = {
|
||||
getNotes,
|
||||
createNote
|
||||
};
|
||||
|
||||
export default noteService;
|
||||
@@ -0,0 +1,88 @@
|
||||
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import noteService from "./noteService";
|
||||
|
||||
const initialState = {
|
||||
notes: [],
|
||||
isError: false,
|
||||
isSuccess: false,
|
||||
isLoading: false,
|
||||
message: "",
|
||||
};
|
||||
|
||||
export const noteSlice = createSlice({
|
||||
name: "note",
|
||||
initialState,
|
||||
reducers: {
|
||||
rest: (stata) => initialState,
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(getNotes.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(getNotes.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.isSuccess = true;
|
||||
state.notes = action.payload;
|
||||
})
|
||||
.addCase(getNotes.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.isError = true;
|
||||
state.message = action.payload;
|
||||
})
|
||||
.addCase(createNote.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(createNote.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.isSuccess = true;
|
||||
state.notes.push(action.payload);
|
||||
})
|
||||
.addCase(createNote.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.isError = true;
|
||||
state.message = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const createNote = createAsyncThunk(
|
||||
"notes/create",
|
||||
async ({ noteText, ticketId }, thunkAPI) => {
|
||||
try {
|
||||
const token = thunkAPI.getState().auth.user.token;
|
||||
return await noteService.createNote(noteText, 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 getNotes = createAsyncThunk(
|
||||
"notes/getAll",
|
||||
async (ticketId, thunkAPI) => {
|
||||
try {
|
||||
const token = thunkAPI.getState().auth.user.token;
|
||||
return await noteService.getNotes(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 { reset } = noteSlice.actions;
|
||||
export default noteSlice.reducer;
|
||||
@@ -1,30 +1,62 @@
|
||||
import { useEffect } from "react";
|
||||
import { FaPlus } from "react-icons/fa";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { closeTicket, getTicket, reset } from "../features/tickets/ticketSlice";
|
||||
import { closeTicket, getTicket } 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";
|
||||
import { getNotes, createNote } from "../features/notes/noteSlice";
|
||||
import NoteItem from "../components/NoteItem";
|
||||
import Modal from "react-modal";
|
||||
|
||||
const customStyles = {
|
||||
content: {
|
||||
width: "600px",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
right: "auto",
|
||||
bottom: "auto",
|
||||
marginRight: "-50%",
|
||||
transform: "translate(-50%,-50%)",
|
||||
position: "relative",
|
||||
},
|
||||
};
|
||||
|
||||
Modal.setAppElement("#root");
|
||||
|
||||
function Ticket() {
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||
const [noteText, setNoteText] = useState("");
|
||||
const { ticket, isLoading, isSuccess, isError, message } = useSelector(
|
||||
(state) => state.tickets
|
||||
);
|
||||
const { notes, isLoading: notesIsLoading } = useSelector(
|
||||
(state) => state.notes
|
||||
);
|
||||
const { ticketId } = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onTicketClose =()=>{
|
||||
dispatch(closeTicket(ticketId))
|
||||
toast.success('Ticket closed')
|
||||
navigate('/tickets')
|
||||
}
|
||||
const onNoteSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(createNote({ noteText, ticketId }));
|
||||
closeModal();
|
||||
};
|
||||
const openModal = () => setModalIsOpen(true);
|
||||
|
||||
const closeModal = () => setModalIsOpen(false);
|
||||
const onTicketClose = () => {
|
||||
dispatch(closeTicket(ticketId));
|
||||
toast.success("Ticket closed");
|
||||
navigate("/tickets");
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isError) toast.error(message);
|
||||
|
||||
dispatch(getTicket(ticketId));
|
||||
dispatch(getNotes(ticketId));
|
||||
}, [isError, message, ticketId]);
|
||||
if (isLoading) return <Spinner />;
|
||||
if (isLoading || notesIsLoading) return <Spinner />;
|
||||
if (isError) return <h3>Something went wrong</h3>;
|
||||
|
||||
return (
|
||||
@@ -48,7 +80,46 @@ function Ticket() {
|
||||
</div>
|
||||
</header>
|
||||
{ticket.status !== "closed" && (
|
||||
<button className="btn btn-block btn-danger"onClick={onTicketClose}>Close Ticket</button>
|
||||
<button onClick={openModal} className="btn">
|
||||
<FaPlus />
|
||||
Add Note
|
||||
</button>
|
||||
)}
|
||||
<Modal
|
||||
isOpen={modalIsOpen}
|
||||
onRequestClose={closeModal}
|
||||
style={customStyles}
|
||||
contentLabel="Add Note"
|
||||
>
|
||||
<h2>Add Note</h2>
|
||||
<button className="btn-close" onClick={closeModal}>
|
||||
X
|
||||
</button>
|
||||
<form onSubmit={onNoteSubmit}>
|
||||
<div className="form-group">
|
||||
<textarea
|
||||
value={noteText}
|
||||
onChange={(e) => setNoteText(e.target.value)}
|
||||
name="noteText"
|
||||
id="noteText"
|
||||
className="form-control"
|
||||
placeholder="Note text"
|
||||
></textarea>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<button className="btn" type="Submit">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
{notes.map((note) => (
|
||||
<NoteItem key={note._id} note={note} />
|
||||
))}
|
||||
{ticket.status !== "closed" && (
|
||||
<button className="btn btn-block btn-danger" onClick={onTicketClose}>
|
||||
Close Ticket
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Generated
+201
-1
@@ -15,7 +15,8 @@
|
||||
"express": "^4.18.2",
|
||||
"express-async-handler": "^1.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mongoose": "^6.7.5"
|
||||
"mongoose": "^6.7.5",
|
||||
"react-modal": "^3.16.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.20"
|
||||
@@ -1417,6 +1418,11 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/exenv": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
|
||||
"integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
@@ -1710,6 +1716,11 @@
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
||||
@@ -1795,6 +1806,17 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -2037,6 +2059,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@@ -2081,6 +2111,16 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -2143,6 +2183,59 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"node_modules/react-modal": {
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
|
||||
"integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
|
||||
"dependencies": {
|
||||
"exenv": "^1.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-lifecycles-compat": "^3.0.0",
|
||||
"warning": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
|
||||
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
@@ -2191,6 +2284,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
@@ -2442,6 +2544,14 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
@@ -3608,6 +3718,11 @@
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
|
||||
},
|
||||
"exenv": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
|
||||
"integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
@@ -3819,6 +3934,11 @@
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"jsonwebtoken": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
||||
@@ -3902,6 +4022,14 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
@@ -4081,6 +4209,11 @@
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@@ -4110,6 +4243,16 @@
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -4154,6 +4297,46 @@
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-modal": {
|
||||
"version": "3.16.1",
|
||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
|
||||
"integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
|
||||
"requires": {
|
||||
"exenv": "^1.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-lifecycles-compat": "^3.0.0",
|
||||
"warning": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
@@ -4182,6 +4365,15 @@
|
||||
"sparse-bitfield": "^3.0.3"
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
@@ -4378,6 +4570,14 @@
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@
|
||||
"express": "^4.18.2",
|
||||
"express-async-handler": "^1.2.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mongoose": "^6.7.5"
|
||||
"mongoose": "^6.7.5",
|
||||
"react-modal": "^3.16.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.20"
|
||||
|
||||
Reference in New Issue
Block a user