Skip to content
This repository has been archived by the owner on Dec 26, 2024. It is now read-only.

[Admin] [Client] Surveys and Reviews #49

Merged
merged 15 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions admin-frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import AdminNotificationPage from "./components/notification/AdminNotificationPa
import VerifyEmailPage from "./components/VerifyEmailPage";
import ViewActiveBookingsPage from "./components/booking/ViewActiveBookingsPage";
import ViewPastBookingsPage from "./components/booking/ViewPastBookingsPage";
import SubmittedSurvey from "./components/survey/SubmittedSurvey";
import SubmittedSurveys from "./components/survey/SubmittedSurveys";
import ActivityThemesPage from "./components/activitytheme/ActivityThemesPage";
import ActivityReviews from "./components/review/ActivityReviews";
import ManageReviewsForActivity from "./components/review/ManageReviewsForActivity";

function App() {
return (
Expand Down Expand Up @@ -194,6 +198,42 @@ function App() {
</ProtectedRoute>
}
/>
<Route
exact
path="/surveys"
element={
<ProtectedRoute>
<SubmittedSurveys />
</ProtectedRoute>
}
/>
<Route
exact
path="/surveys/:surveyId"
element={
<ProtectedRoute>
<SubmittedSurvey />
</ProtectedRoute>
}
/>
<Route
exact
path="/reviews"
element={
<ProtectedRoute>
<ActivityReviews />
</ProtectedRoute>
}
/>
<Route
exact
path="/reviews/activity/:activityId"
element={
<ProtectedRoute>
<ManageReviewsForActivity />
</ProtectedRoute>
}
/>
<Route
exact
path="/verifyEmail/:token"
Expand Down
29 changes: 28 additions & 1 deletion admin-frontend/src/components/navbar/SideNavBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ const bookingManagementList = [
{ "View Active Bookings": "/viewActiveBookings" },
{ "View Past Bookings": "/viewPastBookings" },
];
const surveyManagementList = [
{ "View Submitted Surveys": "/surveys" },
{ "Manage Reviews": "/reviews" },
];

const StyledLink = styled(Link)`
text-decoration: none;
Expand Down Expand Up @@ -74,7 +78,6 @@ const SideNavBar = ({ isSidebarOpen }) => {
User Management
</Typography>
</Box>

<List>
{userManagementList.map((item, index) => (
<StyledDiv key={index}>
Expand Down Expand Up @@ -146,6 +149,30 @@ const SideNavBar = ({ isSidebarOpen }) => {
</ListItem>
</StyledLink>
))}
</List>{" "}
<Divider />
<Box sx={{ paddingLeft: 2, paddingTop: 2 }}>
<Typography
fontWeight={700}
color={theme.palette.primary.main}
fontSize={20}
>
Survey Management
</Typography>
</Box>
<List>
{surveyManagementList.map((item, index) => (
<StyledLink to={item[Object.keys(item)[0]]} key={index}>
<ListItem key={Object.keys(item)[0]} disablePadding>
<ListItemButton
selected={selectedItem === Object.keys(item)[0]}
onClick={() => handleItemClick(Object.keys(item)[0])}
>
<ListItemText primary={Object.keys(item)[0]} />
</ListItemButton>
</ListItem>
</StyledLink>
))}
</List>
</Box>
</Drawer>
Expand Down
54 changes: 54 additions & 0 deletions admin-frontend/src/components/review/ActivityReviews.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from "react";

import styled from "@emotion/styled";
import { useTheme } from "@emotion/react";
import { Badge, CircularProgress, Tabs, Tab, Typography } from "@mui/material";
import { useEffect, useState } from "react";
import {
useActivityStore,
useAdminSurveyResponseStore,
useBookingStore,
useSnackbarStore,
} from "../../zustand/GlobalStore";
import MainBodyContainer from "../common/MainBodyContainer";

import ReviewActivityTable from "./ReviewActivityTable";
function ActivityReviews() {
const theme = useTheme();

const { openSnackbar } = useSnackbarStore();
const { activities, getActivity, isLoading, pendingApprovalActivities } =
useActivityStore();

useEffect(() => {
const fetchData = async () => {
await getActivity();
};
fetchData();
}, [getActivity]);

if (isLoading) {
return <CircularProgress />;
}
return (
<MainBodyContainer
hasBackButton={false}
breadcrumbNames={[]}
breadcrumbLinks={[]}
currentBreadcrumbName={"Manage Reviews"}
>
<Typography
fontSize={25}
fontWeight={700}
noWrap
component="div"
color={theme.palette.primary.main}
>
Manage Reviews
</Typography>
{activities && <ReviewActivityTable activities={activities} />}
</MainBodyContainer>
);
}

export default ActivityReviews;
68 changes: 68 additions & 0 deletions admin-frontend/src/components/review/ManageReviewsForActivity.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";

import { useTheme } from "@emotion/react";
import { CircularProgress, Typography } from "@mui/material";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import {
useActivityStore,
useReviewStore,
useSnackbarStore
} from "../../zustand/GlobalStore";
import MainBodyContainer from "../common/MainBodyContainer";

import ManageReviewsForActivityTable from "./ManageReviewsForActivityTable";

function ManageReviewsForActivity() {
const theme = useTheme();
const { activityId } = useParams();

const { openSnackbar } = useSnackbarStore();
const { activities, getActivity, isLoading, pendingApprovalActivities } =
useActivityStore();

const { reviews, activity, getReviewsForActivity, toggleReviewVisibility } =
useReviewStore();

useEffect(() => {
const fetchData = async () => {
await getReviewsForActivity(activityId);
};
fetchData();
}, [getActivity]);
console.log(reviews);

const handleToggle = async (reviewId) => {
try {
await toggleReviewVisibility(reviewId);
console.log("after handle toggle", reviews)
} catch (error) {
console.error(error);
openSnackbar("An error occurred", "error");
}
};
if (isLoading) {
return <CircularProgress />;
}
return (
<MainBodyContainer
hasBackButton={true}
breadcrumbNames={["Manage Reviews"]}
breadcrumbLinks={["/reviews"]}
currentBreadcrumbName={`For ${activity?.title || "Loading"}`}
>
<Typography
fontSize={25}
fontWeight={700}
noWrap
component="div"
color={theme.palette.primary.main}
>
Manage Reviews For {activity?.title}
</Typography>
{reviews && <ManageReviewsForActivityTable reviews={reviews} handleToggle={handleToggle} />}
</MainBodyContainer>
);
}

export default ManageReviewsForActivity;
170 changes: 170 additions & 0 deletions admin-frontend/src/components/review/ManageReviewsForActivityTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import {
Dialog,
DialogContent,
Rating,
Stack,
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";

const ManageReviewsForActivityTable = ({ reviews, handleToggle }) => {
const [currentTabRows, setCurrentTabRows] = useState(reviews);
const [selectedReview, setSelectedReview] = useState(null);

useEffect(() => {
setCurrentTabRows(reviews);
}, [reviews]);

const columns = [
{
field: "rating",
headerName: "Rating",
flex: 1,
renderCell: (params) => {
const ratingValue = params.value;
return (
<Stack
direction="row"
spacing={1}
justifyContent="center"
alignItems="center"
>
<Rating name="read-only" value={ratingValue} readOnly />
<div>{ratingValue}</div>
</Stack>
);
},
},
{
field: "comment",
headerName: "Client Comment",
flex: 1,
},
{
field: "client",
headerName: "By Client",
flex: 1,
renderCell: (params) => {
return params.row.client.name;
},
},
{
field: "date",
headerName: "Date Submitted",
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 (
<div style={{ display: "flex" }}>
<Typography color="#9F91CC" fontSize={"0.875rem"}>
Submitted on&nbsp;
<span style={{ color: "black" }}>
{formattedDate} at {formattedTime}
</span>
</Typography>
</div>
);
},
},
{
field: "actions",
type: "actions",
flex: 1,
headerName: "Shown",
renderCell: (params) => {
const isChecked = !params.row.hidden;
return (
<div
style={{
display: "flex",
paddingTop: 3,
paddingBottom: 3,
alignItems: "center",
}}
>
<Switch
color="primary"
checked={isChecked}
onChange={() => handleToggle(params.row._id)}
/>
</div>
);
},
},
];

const handleRowClick = (params) => {
setSelectedReview(params.row);
};

const handleClose = () => {
setSelectedReview(null);
};

return (
<Box>
<div style={{ height: 500, width: "99%" }}>
<DataGrid
initialState={{
pagination: {
paginationModel: { pageSize: 25, page: 0 },
},
}}
getRowId={(row) => 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",
},
}}
/>
</div>
<Dialog open={selectedReview !== null} onClose={handleClose} fullWidth>
<DialogContent>
{selectedReview && (
<div>
<Typography variant="h6">Rating:</Typography>
<Rating name="read-only" value={selectedReview.rating} readOnly />
<Typography variant="h6">Comment:</Typography>
<Typography>{selectedReview.comment}</Typography>
<Typography variant="h6">By:</Typography>
<Typography>{selectedReview.client.name}</Typography>
</div>
)}
</DialogContent>
</Dialog>
</Box>
);
};

ManageReviewsForActivityTable.propTypes = {
reviews: PropTypes.array.isRequired,
};

export default ManageReviewsForActivityTable;
Loading