diff --git a/backend/controllers/posts-controller.js b/backend/controllers/posts-controller.js index 51120e88..e275e7fa 100644 --- a/backend/controllers/posts-controller.js +++ b/backend/controllers/posts-controller.js @@ -135,8 +135,11 @@ export const updatePostHandler = async (req, res) => { if (!updatedPost) { return res.status(HTTP_STATUS.NOT_FOUND).json({ message: RESPONSE_MESSAGES.POSTS.NOT_FOUND }); } - - res.status(HTTP_STATUS.OK).json(updatedPost); + // invalidate the redis cache + await deleteDataFromCache(REDIS_KEYS.ALL_POSTS), + await deleteDataFromCache(REDIS_KEYS.FEATURED_POSTS), + await deleteDataFromCache(REDIS_KEYS.LATEST_POSTS), + await res.status(HTTP_STATUS.OK).json(updatedPost); } catch (err) { res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ message: err.message }); } @@ -152,7 +155,11 @@ export const deletePostByIdHandler = async (req, res) => { } await User.findByIdAndUpdate(post.authorId, { $pull: { posts: req.params.id } }); - res.status(HTTP_STATUS.OK).json({ message: RESPONSE_MESSAGES.POSTS.DELETED }); + // invalidate the redis cache + await deleteDataFromCache(REDIS_KEYS.ALL_POSTS), + await deleteDataFromCache(REDIS_KEYS.FEATURED_POSTS), + await deleteDataFromCache(REDIS_KEYS.LATEST_POSTS), + res.status(HTTP_STATUS.OK).json({ message: RESPONSE_MESSAGES.POSTS.DELETED }); } catch (err) { res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ message: err.message }); } diff --git a/backend/controllers/user-controller.js b/backend/controllers/user-controller.js index dd159131..ac846f59 100644 --- a/backend/controllers/user-controller.js +++ b/backend/controllers/user-controller.js @@ -3,10 +3,13 @@ import User from '../models/user.js'; export const getAllUserHandler = async (req, res) => { try { - const users = await User.find().select('_id name email'); + const users = await User.find().select('_id fullName role email'); return res.status(HTTP_STATUS.OK).json({ users }); } catch (error) { - res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ message: error.message }); + console.log(error); + res + .status(HTTP_STATUS.INTERNAL_SERVER_ERROR) + .json({ message: RESPONSE_MESSAGES.COMMON.INTERNAL_SERVER_ERROR }); } }; @@ -14,7 +17,7 @@ export const changeUserRoleHandler = async (req, res) => { try { const userId = req.params.userId; const { role } = req.body; - if (role === 'user' || role === 'admin') { + if (role === 'USER' || role === 'ADMIN') { const user = await User.findById(userId); if (!user) return res @@ -29,6 +32,7 @@ export const changeUserRoleHandler = async (req, res) => { } return res.status(HTTP_STATUS.OK).json({ message: RESPONSE_MESSAGES.USERS.UPDATE }); } catch (error) { + console.log(error); res .status(HTTP_STATUS.INTERNAL_SERVER_ERROR) .json({ message: RESPONSE_MESSAGES.COMMON.INTERNAL_SERVER_ERROR, error: error }); @@ -45,6 +49,7 @@ export const deleteUserHandler = async (req, res) => { .json({ message: RESPONSE_MESSAGES.USERS.USER_NOT_EXISTS }); res.status(HTTP_STATUS.NO_CONTENT).json({ message: RESPONSE_MESSAGES.USERS.DELETED }); } catch (error) { + console.log(error); res .status(HTTP_STATUS.INTERNAL_SERVER_ERROR) .json({ message: RESPONSE_MESSAGES.COMMON.INTERNAL_SERVER_ERROR, error: error }); diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index fc012a59..5549f0ed 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -6,10 +6,10 @@ export default [ { languageOptions: { globals: globals.browser } }, ...tseslint.configs.recommended, pluginReactConfig, - { - rules: { - 'react/react-in-jsx-scope': 'off' , - 'react/no-unescaped-entities': 'off' - } - } + { + rules: { + 'react/react-in-jsx-scope': 'off', + 'react/no-unescaped-entities': 'off', + }, + }, ]; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index acde5d8c..878af5fa 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -13,6 +13,7 @@ import UnprotectedRoute from './components/unprotected-route'; import { useLayoutEffect } from 'react'; import RequireAuth from './components/require-auth'; import useThemeClass from './utils/theme-changer'; +import AdminContainer from './components/admin-container'; function App() { useLayoutEffect(() => { @@ -34,8 +35,10 @@ function App() { } /> }> - } /> - } /> + }> + } /> + } /> + } /> diff --git a/frontend/src/components/admin-sidebar.tsx b/frontend/src/components/admin-sidebar.tsx index 2b7cd983..698ae456 100644 --- a/frontend/src/components/admin-sidebar.tsx +++ b/frontend/src/components/admin-sidebar.tsx @@ -1,4 +1,4 @@ -import { NavLink } from 'react-router-dom'; +import { NavLink, useNavigate } from 'react-router-dom'; import UserIcon from '@/assets/svg/user-icon'; import BlogIcon from '@/assets/svg/blog-icon'; import BarIcons from '@/assets/svg/bars-icon'; @@ -8,6 +8,8 @@ import CloseIcon from '@/assets/svg/close-icon'; const AdminSidebar = () => { const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const navigate = useNavigate(); + return ( <>
-

WanderLust

+

navigate('/')} + className="cursor-pointer text-xl font-medium text-light-title dark:text-dark-title" + > + WanderLust +

{
- - - -
- - + + + +
+ +
diff --git a/frontend/src/layouts/header-layout.tsx b/frontend/src/layouts/header-layout.tsx index 87511975..43349af0 100644 --- a/frontend/src/layouts/header-layout.tsx +++ b/frontend/src/layouts/header-layout.tsx @@ -16,6 +16,7 @@ import { Link } from 'react-router-dom'; function header() { const navigate = useNavigate(); const { token, loading } = useAuthData(); + const user = userState.getUser(); const handleLogout = async () => { try { @@ -73,6 +74,17 @@ function header() { ) : token ? (
+ {user?.role === 'ADMIN' && ( + + )} + - -
-
+ {posts?.map((post: Post) => { + return ( +
+ +
+

+ {post?.title} +

+

+ {post?.description} +

+

+ {post?.authorName} • {formatPostTime(post?.timeOfPost)} +

+
+
+ + +
+
+ ); + })}
diff --git a/frontend/src/pages/admin-users.tsx b/frontend/src/pages/admin-users.tsx index 3a815bec..7740052b 100644 --- a/frontend/src/pages/admin-users.tsx +++ b/frontend/src/pages/admin-users.tsx @@ -1,4 +1,48 @@ +import axiosInstance from '@/helpers/axios-instance'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + +enum role { + admin = 'ADMIN', + user = 'USER', +} + +type User = { + _id: string; + fullName: string; + role: role; + email: string; +}; + const AdminUsers = () => { + const [users, setUsers] = useState([]); + + const fetchData = async () => { + try { + const response = await axiosInstance.get('/api/user'); + setUsers(response?.data?.users); + } catch (error) { + toast.error('Something went wrong! Please try again!'); + } + }; + + const handleClick = async (userId: string, role: role) => { + try { + const response = await axiosInstance.patch('/api/user/' + userId, { role: role }); + if (response.status === 200) { + fetchData(); + toast.success('User updated successfully!'); + } + } catch (error) { + toast.error('Something went wrong! Please try again later.'); + } + }; + + useEffect(() => { + fetchData(); + }, []); + return ( <>
@@ -6,17 +50,39 @@ const AdminUsers = () => { Users
-
-
-

Hemant

-

- hemant412@gmail.com -

-
- -
+ {users?.map((user: User) => { + return ( +
+
+

+ {user?.fullName} +

+

+ {user?.email} +

+
+ {user.role === role.admin && ( + + )} + {user.role === role.user && ( + + )} +
+ ); + })}
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 7e00ccf5..c289b7f5 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ import colors from 'tailwindcss/colors'; -import defaultTheme from 'tailwindcss/defaultTheme' +import defaultTheme from 'tailwindcss/defaultTheme'; export default { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], @@ -20,7 +20,7 @@ export default { field: colors.slate[900], button: colors.slate[700], textInField: colors.slate[50], - textColor: colors.slate[50] + textColor: colors.slate[50], }, light: { DEFAULT: colors.slate[50], diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 22763145..eb9e8cdf 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,4 +1,3 @@ - { "compilerOptions": { "baseUrl": ".",