diff --git a/admin-frontend/src/App.js b/admin-frontend/src/App.js index 2947512..0939a07 100644 --- a/admin-frontend/src/App.js +++ b/admin-frontend/src/App.js @@ -29,6 +29,8 @@ import ActivityThemesPage from "./components/activitytheme/ActivityThemesPage"; import ActivityReviews from "./components/review/ActivityReviews"; import ManageReviewsForActivity from "./components/review/ManageReviewsForActivity"; import AdminChatpage from "./components/Chat/AdminChatPage"; +import ManageTestimonials from "./components/testimonial/ManageTestimonials.jsx"; +import TestimonialDetails from "./components/testimonial/TestimonialDetails.jsx"; import VendorDetails from "./components/vendor/VendorDetails"; function App() { @@ -101,7 +103,7 @@ function App() { } /> - } - /> + /> */} } /> + + + + + } + /> + + + + } + /> { - const date = new Date(value); - const options = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - }; - const formattedDate = date.toLocaleDateString("en-SG", options); - return formattedDate; -}; function SubmittedSurvey() { const theme = useTheme(); const { surveyId } = useParams(); const { survey, getSurveyDetails, isLoading } = useAdminSurveyResponseStore(); + const { hasTestimonialForSurvey, createTestimonialForSurvey } = + useTestimonialStore(); const { openSnackbar } = useSnackbarStore(); + const [hasTestimonial, setHasTestimonial] = useState(); + const [testimonial, setTestimonial] = useState(null); + const navigate = useNavigate(); useEffect(() => { const get = async () => { try { await getSurveyDetails(surveyId); + const testimonialData = await hasTestimonialForSurvey(surveyId); + setHasTestimonial(testimonialData.hasTestimonial); + if (testimonialData.hasTestimonial) { + setTestimonial(testimonialData.testimonial); + } } catch (err) { openSnackbar("Error retrieving survey.", "error"); } @@ -50,6 +40,17 @@ function SubmittedSurvey() { get(); }, []); + const handleCreateTestimonial = async () => { + try { + const newT = await createTestimonialForSurvey(surveyId); + navigate(`/testimonials/${newT._id}`); + openSnackbar("Created new testimonial.", "success"); + } catch (err) { + console.log(err) + openSnackbar("Error creating testimonial.", "error"); + } + }; + if (isLoading) { return ; } @@ -100,7 +101,14 @@ function SubmittedSurvey() { - {survey && } + {survey && ( + handleCreateTestimonial()} + /> + )} ); } diff --git a/admin-frontend/src/components/survey/SurveyDetails.jsx b/admin-frontend/src/components/survey/SurveyDetails.jsx index 81bdb52..afc9ab0 100644 --- a/admin-frontend/src/components/survey/SurveyDetails.jsx +++ b/admin-frontend/src/components/survey/SurveyDetails.jsx @@ -1,12 +1,8 @@ -import React from "react"; import styled from "@emotion/styled"; +import { LinkOff, MoodOutlined } from "@mui/icons-material"; +import AddIcon from "@mui/icons-material/Add"; import { - Box, Button, - Checkbox, - CircularProgress, - Divider, - FormControlLabel, Grid, Paper, Rating, @@ -14,8 +10,7 @@ import { TextField, Typography, } from "@mui/material"; -import InfoIcon from "@mui/icons-material/Info"; -import AddIcon from "@mui/icons-material/Add"; +import React from "react"; const StyledPaper = styled(Paper)` padding: 20px; @@ -25,8 +20,12 @@ const StyledPaper = styled(Paper)` box-shadow: 3px 3px 0px 0px rgb(159, 145, 204, 40%); `; -function SurveyDetails({ surveyData }) { - console.log(surveyData); +function SurveyDetails({ + surveyData, + hasTestimonial, + testimonial, + handleCreateTestimonial, +}) { return ( <> @@ -154,9 +153,25 @@ function SurveyDetails({ surveyData }) { Testimonial - + {hasTestimonial ? ( + + ) : ( + + )} diff --git a/admin-frontend/src/components/testimonial/ManageTestimonials.jsx b/admin-frontend/src/components/testimonial/ManageTestimonials.jsx new file mode 100644 index 0000000..d4e3d72 --- /dev/null +++ b/admin-frontend/src/components/testimonial/ManageTestimonials.jsx @@ -0,0 +1,74 @@ +import { useTheme } from "@emotion/react"; +import { CircularProgress, Tab, Tabs, Typography } from "@mui/material"; +import { useEffect, useState } from "react"; +import { + useSnackbarStore, + useTestimonialStore, +} from "../../zustand/GlobalStore"; +import MainBodyContainer from "../common/MainBodyContainer"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; +import TestimonialsTable from "./TestimonialsTable"; + +function ManageTestimonials() { + const theme = useTheme(); + + const { openSnackbar } = useSnackbarStore(); + const { + testimonials, + getAllTestimonials, + isLoading, + toggleTestimonialVisibility, + } = useTestimonialStore(); + + const handleToggle = async (testimonialId) => { + try { + await toggleTestimonialVisibility(testimonialId); + } catch (error) { + console.error(error); + openSnackbar("An error occurred", "error"); + } + }; + + useEffect(() => { + const fetchData = async () => { + try { + await getAllTestimonials(); + } catch (err) { + console.error(err); + openSnackbar("Error when retrieving testimonials.", "error"); + } + }; + fetchData(); + }, [getAllTestimonials]); + + if (isLoading) { + return ; + } + return ( + + + Manage Testimonials + + {testimonials && ( + + )} + + ); +} + +export default ManageTestimonials; diff --git a/admin-frontend/src/components/testimonial/TestimonialDetails.jsx b/admin-frontend/src/components/testimonial/TestimonialDetails.jsx new file mode 100644 index 0000000..443f699 --- /dev/null +++ b/admin-frontend/src/components/testimonial/TestimonialDetails.jsx @@ -0,0 +1,216 @@ +import React, { useEffect, useState } from "react"; +import { useTheme } from "@emotion/react"; +import { + Avatar, + Button, // Import Button + Card, + CardContent, + CircularProgress, + Divider, // Import Divider + Grid, + Paper, + Typography, + TextareaAutosize, + TextField, + Stack, + Box, + Switch, + FormControlLabel, +} from "@mui/material"; +import { + useSnackbarStore, + useTestimonialStore, +} from "../../zustand/GlobalStore"; +import MainBodyContainer from "../common/MainBodyContainer"; +import FormatQuoteIcon from "@mui/icons-material/FormatQuote"; +import { useParams } from "react-router-dom"; +import styled from "@emotion/styled"; +import TestimonialMenuButton from "./TestimonialMenuButton"; + +function TestimonialDetails() { + const theme = useTheme(); + const { testimonialId } = useParams(); + + const { openSnackbar } = useSnackbarStore(); + const { testimonial, getTestimonialById, isLoading, updateTestimonialById } = + useTestimonialStore(); + + const StyledPaper = styled(Paper)` + padding: 20px; + padding-top: 6px; + border-radius: 10px; + border: 1px solid rgb(159, 145, 204); + box-shadow: 3px 3px 0px 0px rgb(159, 145, 204, 40%); + `; + + const handleUpdate = async () => { + try { + await updateTestimonialById(testimonialId, testimonialDetail); + openSnackbar("Sucessfully updated testimonial.", "success"); + } catch (error) { + console.log(error); + openSnackbar(error.message, "error"); + } + }; + + const [testimonialDetail, setTestimonialDetail] = useState({ + testimonialBody: "", + displayName: "", + clientName: "", + hidden: true, + }); + + useEffect(() => { + const fetchData = async () => { + try { + const t = await getTestimonialById(testimonialId); + + setTestimonialDetail(t); + } catch (err) { + console.error(err); + openSnackbar("Error when retrieving testimonial.", "error"); + } + }; + fetchData(); + }, [getTestimonialById, testimonialId]); + + if (isLoading) { + return ; + } + + return ( + + + + Testimonial Details + + + + + {testimonial && ( +
+ + + setTestimonialDetail({ + ...testimonialDetail, + testimonialBody: e.target.value, + }) + } + /> + + setTestimonialDetail({ + ...testimonialDetail, + displayName: e.target.value, + }) + } + /> + + setTestimonialDetail({ + ...testimonialDetail, + clientName: e.target.value, + }) + } + /> + + setTestimonialDetail({ + ...testimonialDetail, + hidden: !testimonialDetail.hidden, + }) + } + /> + } + /> + + + + + + + Current Testimonial + + + + + + + +
+ + {testimonial.testimonialBody} + +
+ + + - {testimonial.displayName}, {testimonial.clientName} + +
+
+
+
+ )} +
+ ); +} + +export default TestimonialDetails; diff --git a/admin-frontend/src/components/testimonial/TestimonialMenuButton.jsx b/admin-frontend/src/components/testimonial/TestimonialMenuButton.jsx new file mode 100644 index 0000000..3ec1934 --- /dev/null +++ b/admin-frontend/src/components/testimonial/TestimonialMenuButton.jsx @@ -0,0 +1,112 @@ +import React, { useState } from "react"; +import { + Button, + IconButton, + Menu, + MenuItem, + Dialog, + DialogTitle, + DialogContent, + DialogActions, +} from "@mui/material"; +import MoreVertIcon from "@mui/icons-material/MoreVert"; +import DeleteIcon from "@mui/icons-material/Delete"; +import ArticleIcon from "@mui/icons-material/Article"; +import { KeyboardArrowDown } from "@mui/icons-material"; +import { useSnackbarStore, useTestimonialStore } from "../../zustand/GlobalStore"; +import { useNavigate } from "react-router-dom"; + +function TestimonialMenuButton({ testimonial }) { + const [anchorEl, setAnchorEl] = useState(null); + const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false); + const { deleteTestimonial } = useTestimonialStore(); + const {openSnackbar} = useSnackbarStore() + const navigate = useNavigate(); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleDelete = () => { + setDeleteDialogOpen(true); + handleClose(); + }; + + const handleViewOriginalSurvey = () => { + // Add your view original survey method logic here + navigate(`/surveys/${testimonial.survey._id}`) + handleClose(); + }; + + const handleDeleteConfirmed = async () => { + try { + await deleteTestimonial(testimonial._id); + setDeleteDialogOpen(false); + openSnackbar("Deleted.", "success") + navigate("/testimonials") + } catch (error) { + console.error(error); + openSnackbar(error.message, "error") + } + }; + + const handleDeleteCanceled = () => { + // User canceled the deletion, close the dialog + setDeleteDialogOpen(false); + }; + + return ( +
+ + + + + + Delete + + + + View Original Survey + + + + + Confirm Deletion + +

Are you sure you want to delete this testimonial?

+

+ You can re-create the testimonial from the original survey anytime. +

+
+ + + + +
+
+ ); +} + + +export default TestimonialMenuButton; diff --git a/admin-frontend/src/components/testimonial/TestimonialsTable.jsx b/admin-frontend/src/components/testimonial/TestimonialsTable.jsx new file mode 100644 index 0000000..772d015 --- /dev/null +++ b/admin-frontend/src/components/testimonial/TestimonialsTable.jsx @@ -0,0 +1,179 @@ +import { Article } from "@mui/icons-material"; +import { + Button, + Switch, + Typography +} from "@mui/material"; + +import Box from "@mui/material/Box"; +import { DataGrid, GridToolbarFilterButton } from "@mui/x-data-grid"; +import PropTypes from "prop-types"; +import React, { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; + +const TestimonialsTable = ({ testimonials, handleToggle }) => { + const [currentTabRows, setCurrentTabRows] = useState(testimonials); + const [selectedReview, setSelectedReview] = useState(null); + const navigate = useNavigate(); + + useEffect(() => { + setCurrentTabRows(testimonials); + }, [testimonials]); + + const columns = [ + { + field: "testimonialBody", + headerName: "Client Testimonial", + flex: 1, + }, + { + field: "displayName", + headerName: "Client Display Name", + flex: 1, + }, + { + field: "created", + headerName: "Created", + flex: 1, + renderCell: (params) => { + const date = new Date(params.value); + const formattedDate = date.toLocaleDateString(undefined, { + year: "2-digit", + month: "2-digit", + day: "2-digit", + }); + const formattedTime = date.toLocaleTimeString(undefined, { + hour: "numeric", + minute: "numeric", + hour12: true, + }); + return ( +
+ + Created on  + + {formattedDate} at {formattedTime} + + +
+ ); + }, + }, + { + field: "updated", + headerName: "Updated", + flex: 1, + renderCell: (params) => { + const date = new Date(params.value); + const formattedDate = date.toLocaleDateString(undefined, { + year: "2-digit", + month: "2-digit", + day: "2-digit", + }); + const formattedTime = date.toLocaleTimeString(undefined, { + hour: "numeric", + minute: "numeric", + hour12: true, + }); + return ( +
+ + Updated on  + + {formattedDate} at {formattedTime} + + +
+ ); + }, + }, + { + field: "survey", + flex: 1, + type: "actions", + headerName: "Original Survey", + renderCell: (params) => { + const survey = params.row.survey; + const surveyId = survey._id; + const surveyLink = `/surveys/${surveyId}`; + return ( + + ); + }, + }, + + { + field: "hidden", + type: "actions", + flex: 1, + headerName: "Shown", + renderCell: (params) => { + const isChecked = !params.row.hidden; + return ( +
+ handleToggle(params.row._id)} + /> +
+ ); + }, + }, + ]; + + const handleRowClick = (params) => { + navigate(`${params.row._id}`); + }; + + const handleClose = () => { + setSelectedReview(null); + }; + + return ( + +
+ row._id} + rows={currentTabRows} + columns={columns} + slots={{ + toolbar: GridToolbarFilterButton, + }} + disableRowSelectionOnClick + getRowHeight={() => "auto"} + onRowClick={handleRowClick} + sx={{ + borderRadius: "10px", + boxShadow: "4px 4px 0px 0px rgb(159 145 204 / 40%)", + border: "none", + backgroundColor: "white", + "& .MuiDataGrid-cell:hover": { + cursor: "pointer", + }, + }} + /> +
+
+ ); +}; + +TestimonialsTable.propTypes = { + testimonials: PropTypes.array.isRequired, +}; + +export default TestimonialsTable; diff --git a/admin-frontend/src/zustand/GlobalStore.js b/admin-frontend/src/zustand/GlobalStore.js index 1dcd4b2..5cd324a 100644 --- a/admin-frontend/src/zustand/GlobalStore.js +++ b/admin-frontend/src/zustand/GlobalStore.js @@ -774,6 +774,122 @@ export const useReviewStore = create((set) => ({ }, })); +export const useTestimonialStore = create((set) => ({ + testimonials: [], + testimonial: null, + isLoading: true, + getAllTestimonials: async () => { + try { + const response = await AxiosConnect.get(`/testimonial/`); + console.log("getAllTestimonials", response.data); + set({ + testimonials: response.data, + isLoading: false, + }); + } catch (error) { + console.error(error); + throw error; + } + }, + getTestimonialById: async (testimonialId) => { + try { + const response = await AxiosConnect.get(`/testimonial/${testimonialId}`); + console.log("getTestimonialById", response.data); + set({ + testimonial: response.data.testimonial, + isLoading: false, + }); + return response.data.testimonial; + } catch (error) { + console.error(error); + throw error; + } + }, + updateTestimonialById: async (testimonialId, updateData) => { + try { + const response = await AxiosConnect.patch( + `/testimonial`, + testimonialId, + updateData, + ); + console.log("updateTestimonialById", response.data); + set({ + testimonial: response.data.testimonial, + isLoading: false, + }); + return response.data.testimonial; + } catch (error) { + console.error(error); + throw error; + } + }, + hasTestimonialForSurvey: async (surveyId) => { + try { + const response = await AxiosConnect.get( + `/testimonial/survey/${surveyId}`, + ); + console.log("hasTestimonialForSurvey", response.data); + + return response.data; + } catch (error) { + console.error(error); + throw error; + } + }, + createTestimonialForSurvey: async (surveyId) => { + try { + const response = await AxiosConnect.post(`/testimonial/create`, { + surveyId, + }); + console.log("createTestimonialForSurvey", response.data); + + return response.data.testimonial; + } catch (error) { + console.error(error); + throw error; + } + }, + deleteTestimonial: async (testimonialId) => { + try { + const response = await AxiosConnect.delete( + `/testimonial/${testimonialId}`, + ); + console.log("deleteTestimonial", response.data); + } catch (error) { + console.error(error); + throw error; + } + }, + toggleTestimonialVisibility: async (testimonialId) => { + try { + const response = await AxiosConnect.post( + `/testimonial/${testimonialId}/toggleVisibility`, + ); + const updatedTestimonial = response.data; + + set((state) => { + const updatedTestimonials = state.testimonials.map((t) => { + if (t._id === updatedTestimonial._id) { + return { + ...t, + hidden: updatedTestimonial.hidden, + }; + } + return t; + }); + return { + ...state, + testimonials: updatedTestimonials, + isLoading: false, + }; + }); + } catch (error) { + console.error(error); + throw error; + } + }, +})); + export const useImageUploadTestStore = create((set) => ({ testActivities: [], setTestActivities: (newActivityList) => { diff --git a/server/controller/testimonialController.js b/server/controller/testimonialController.js new file mode 100644 index 0000000..5c04c95 --- /dev/null +++ b/server/controller/testimonialController.js @@ -0,0 +1,193 @@ +import Testimonial from "../model/testimonialModel.js"; +import Survey from "../model/adminSurveyResponseModel.js"; + +/* + * Get the testimonial based on survey + */ +export const getTestimonialForSurvey = async (req, res) => { + try { + const client = req.user; + + const surveyId = req.params.surveyId; + const survey = await Survey.findById(surveyId); + + // if (!client._id.equals(survey.clientId)) { + // return res.status(403).json({ message: "Unauthorised." }); + // } + + const testimonial = await Testimonial.findOne({ survey: surveyId }); + + return res + .status(200) + .json({ testimonial: testimonial, hasTestimonial: !!testimonial }); + } catch (err) { + console.error(err); + return res.status(500).send({ message: "Server error" }); + } +}; + +export const getTestimonialById = async (req, res) => { + try { + const client = req.user; + + const testimonialId = req.params.testimonialId; + + const testimonial = await Testimonial.findById(testimonialId).populate("survey"); + + return res.status(200).json({ testimonial: testimonial }); + } catch (err) { + console.error(err); + return res.status(500).send({ message: "Server error" }); + } +}; + +export const getAllTestimonials = async (req, res) => { + try { + const testimonials = await Testimonial.find().populate({ + path: "survey", + // populate: { + // path: "linkedVendor", + // select: "companyName companyEmail", + // }, + }); + + return res.status(200).json(testimonials); + } catch (err) { + console.error(err); + return res.status(500).send({ message: "Server error" }); + } +}; + +export const toggleTestimonialVisibility = async (req, res) => { + try { + const testimonialId = req.params.testimonialId; + console.log("toggleTestimonialVisibility", testimonialId); + const testimonial = await Testimonial.findById(testimonialId); + + if (!testimonial) { + return res.status(404).json({ message: "Testimonial not found." }); + } + testimonial.hidden = !testimonial.hidden; + await testimonial.save(); + + res.status(200).json(testimonial); + } catch (error) { + console.error(error); + res.status(500).json({ + message: "Server Error! Unable to toggle testimonial visibility.", + error: error.message, + }); + } +}; + +export const createTestimonialFromSurvey = async (req, res) => { + try { + const { surveyId, testimonial, displayName } = req.body; + const foundTestimonial = await Testimonial.findOne({ + survey: surveyId, + }); + + if (foundTestimonial) { + return res.status(409).json({ message: "Testimonial already exists." }); + } + + const survey = await Survey.findById(surveyId).populate({ + path: "booking", + populate: { + path: "clientId", + select: "name", + }, + }); + + if (!survey) { + return res.status(404).json({ message: "Survey not found." }); + } + + console.log(survey.booking.clientId); + + const newTestimonial = new Testimonial({ + survey: surveyId, + testimonialBody: testimonial || survey.testimonial, + displayName: displayName || survey.displayName, + clientName: survey.booking.clientId.name, + hidden: true, + created: new Date(), + }); + + await newTestimonial.save(); + + res.status(201).json({ + message: "Testimonial created successfully", + testimonial: newTestimonial, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + message: "Server Error! Unable to create testimonial.", + error: error.message, + }); + } +}; + +export const updateTestimonialById = async (req, res) => { + try { + const testimonialId = req.params.testimonialId; + const { testimonialBody, displayName, clientName, hidden } = req.body; + const foundTestimonial = await Testimonial.findById(testimonialId); + + if (!foundTestimonial) { + return res.status(404).json({ message: "Testimonial not found." }); + } + + if (testimonialBody) { + foundTestimonial.testimonialBody = testimonialBody; + } + + if (displayName) { + foundTestimonial.displayName = displayName; + } + + if (clientName) { + foundTestimonial.clientName = clientName; + } + + if (hidden !== undefined) { + foundTestimonial.hidden = hidden; + } + + foundTestimonial.updated = new Date(); + + await foundTestimonial.save(); + + res.status(200).json({ + message: "Testimonial updated successfully", + testimonial: foundTestimonial, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + message: "Server Error! Unable to update testimonial.", + error: error.message, + }); + } +}; + +export const deleteTestimonialById = async (req, res) => { + try { + const testimonialId = req.params.testimonialId; + const deletedTestimonial = await Testimonial.findByIdAndDelete(testimonialId); + + if (!deletedTestimonial) { + return res.status(404).json({ message: "Testimonial not found." }); + } + + res.status(200).json({ message: "Testimonial deleted successfully" }); + } catch (error) { + console.error(error); + res.status(500).json({ + message: "Server Error! Unable to delete testimonial.", + error: error.message, + }); + } +}; + diff --git a/server/model/testimonialModel.js b/server/model/testimonialModel.js index 0453e22..d345781 100644 --- a/server/model/testimonialModel.js +++ b/server/model/testimonialModel.js @@ -1,13 +1,20 @@ import mongoose from "mongoose"; const testimonialSchema = new mongoose.Schema({ - id: { type: mongoose.Schema.Types.ObjectId, required: true }, + survey: { + type: mongoose.Schema.Types.ObjectId, + ref: "AdminSurveyResponse", + required: true, + }, + testimonialBody: { type: String, required: true }, displayName: { type: String, required: true }, - isShown: { type: Boolean, required: true, default: false }, + clientName: { type: String, required: true }, + hidden: { type: Boolean, required: true, default: true }, created: { type: Date, required: true }, + updated: { type: Date, required: true, default: Date.now }, }); const Testimonial = mongoose.model("Testimonial", testimonialSchema); -module.exports = Testimonial; +export default Testimonial; diff --git a/server/routes/gleekAdmin/testimonialRoute.js b/server/routes/gleekAdmin/testimonialRoute.js new file mode 100644 index 0000000..a67ae8f --- /dev/null +++ b/server/routes/gleekAdmin/testimonialRoute.js @@ -0,0 +1,27 @@ +import express from "express"; + +const router = express.Router(); +import adminAuth from "../../middleware/adminAuth.js"; +import { + createTestimonialFromSurvey, + deleteTestimonialById, + getAllTestimonials, + getTestimonialById, + getTestimonialForSurvey, + toggleTestimonialVisibility, + updateTestimonialById, +} from "../../controller/testimonialController.js"; + +router.get("/", adminAuth, getAllTestimonials); + +router.get("/:testimonialId", getTestimonialById); +router.patch("/:testimonialId", updateTestimonialById); +router.delete("/:testimonialId", deleteTestimonialById); + +router.post("/:testimonialId/toggleVisibility", toggleTestimonialVisibility); + +router.get("/survey/:surveyId", getTestimonialForSurvey); + +router.post("/create", adminAuth, createTestimonialFromSurvey); + +export default router; diff --git a/server/server.js b/server/server.js index 39f2d76..27f65d2 100644 --- a/server/server.js +++ b/server/server.js @@ -11,6 +11,7 @@ import vendorRoutes from "./routes/gleekAdmin/vendorRoute.js"; import surveyRoutes from "./routes/gleekAdmin/surveyRoute.js"; import reviewRoutes from "./routes/gleekAdmin/reviewRoute.js"; import bookingRoutes from "./routes/gleekAdmin/bookingRoute.js"; +import testimonialRoutes from "./routes/gleekAdmin/testimonialRoute.js"; import client from "./routes/gleekAdmin/client.js"; import activityTestController from "./controller/activityTestController.js"; import notificationRoutes from "./routes/notificationRoute.js"; @@ -55,6 +56,7 @@ app.use("/client", client); app.use("/booking", bookingRoutes); app.use("/survey", surveyRoutes); app.use("/review", reviewRoutes); +app.use("/testimonial", testimonialRoutes); /** * For Client application