From cdd6a0e61542aeef0951a2b350ddfaae57d2f65c Mon Sep 17 00:00:00 2001 From: soleil00 Date: Mon, 29 Jul 2024 17:56:13 +0200 Subject: [PATCH] extended notifcation componet displaying notification for both user and seller --- src/App.tsx | 9 +- src/components/cards/Notification.tsx | 16 ++- src/components/cards/ProductCard.tsx | 6 +- src/components/common/header/Header.tsx | 26 ++++- .../user-notifications/UserNotifcations.tsx | 96 ++++++++++++++++ .../UserNotificationDetail.tsx | 70 ++++++++++++ src/redux/reducers/notificationSlice.ts | 37 ++++-- src/routes/AppRoutes.tsx | 107 ++++++++++-------- src/utils/currentuser.ts | 3 +- 9 files changed, 302 insertions(+), 68 deletions(-) create mode 100644 src/components/common/user-notifications/UserNotifcations.tsx create mode 100644 src/components/common/user-notifications/UserNotificationDetail.tsx diff --git a/src/App.tsx b/src/App.tsx index 38ee2d3..c156b3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,7 +13,10 @@ import { disconnectFromSocket, getSocket, } from "./utils/socket"; -import { getUserNotifications } from "./redux/reducers/notificationSlice"; +import { + getUserNotifications, + handleCurrentUser, +} from "./redux/reducers/notificationSlice"; import UpdatePasswordmod from "./components/password/updateModal"; import PasswordPopup from "./components/password/PasswordPopup"; @@ -39,6 +42,10 @@ const App: React.FC = () => { dispatch(getUserNotifications()); }, [dispatch]); + React.useEffect(() => { + dispatch(handleCurrentUser()); + }, []); + const { isPasswordExpired } = useAppSelector((state) => state.updatePin); return ( diff --git a/src/components/cards/Notification.tsx b/src/components/cards/Notification.tsx index 135eb4a..0bcc477 100644 --- a/src/components/cards/Notification.tsx +++ b/src/components/cards/Notification.tsx @@ -17,7 +17,9 @@ export const NotificationPopup: React.FC = ({ handleClose, open, }) => { - const { notifications } = useAppSelector((state) => state.notifications); + const { notifications, currentUser } = useAppSelector( + (state) => state.notifications, + ); return ( = ({ notifications.slice(0, 5).map((notification, index) => ( <> = ({ )} diff --git a/src/components/cards/ProductCard.tsx b/src/components/cards/ProductCard.tsx index 2278be3..8daa606 100644 --- a/src/components/cards/ProductCard.tsx +++ b/src/components/cards/ProductCard.tsx @@ -191,7 +191,7 @@ const ProductCard: React.FC = ({ product }) => { product navigate(`/products/${product.id}`)} /> @@ -258,7 +258,7 @@ const ProductCard: React.FC = ({ product }) => { {name}
-

+

{/* ${formatPrice(product.price)} */} {product.price} {' '} @@ -266,7 +266,7 @@ const ProductCard: React.FC = ({ product }) => {

= ({ searchQuery, setSearchQuery }) => { const userInfo = localStorage.getItem("accessToken") ? JSON.parse(atob(localStorage.getItem("accessToken")!.split(".")[1])) : null; + const [target, setTarget] = useState(null); const dispatch = useAppDispatch(); const navigate = useNavigate(); + const { unreadCount, currentUser } = useAppSelector( + (state) => state.notifications, + ); const handleSelectSuggestion = (suggestion: string) => { setSearchQuery(suggestion); @@ -102,6 +108,10 @@ const Header: React.FC = ({ searchQuery, setSearchQuery }) => { navigate(`/products?query=${encodeURIComponent(searchQuery)}`); }; + const handleClose = () => { + setTarget(null); + }; + return ( = ({ searchQuery, setSearchQuery }) => { )}
+
setTarget(e.currentTarget)} + > + +

+ {unreadCount} +

+
{localStorage.getItem("accessToken") && userInfo.roleId === 2 ? ( @@ -198,6 +217,11 @@ const Header: React.FC = ({ searchQuery, setSearchQuery }) => { )}
+ ); }; diff --git a/src/components/common/user-notifications/UserNotifcations.tsx b/src/components/common/user-notifications/UserNotifcations.tsx new file mode 100644 index 0000000..f7ac55c --- /dev/null +++ b/src/components/common/user-notifications/UserNotifcations.tsx @@ -0,0 +1,96 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { FaEnvelope, FaEnvelopeOpenText } from "react-icons/fa"; +import { Button } from "@mui/material"; + +import { useAppSelector } from "../../../redux/hooks"; +import { getCurrentUser } from "../../../utils/currentuser"; + +const UserNotifications = () => { + const { notifications } = useAppSelector((state) => state.notifications); + + const formatDate = (dateString: Date) => { + const date = new Date(dateString); + const options = { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + }; + // @ts-ignore + return date.toLocaleDateString("en-US", options); + }; + + const sortedNotifications = notifications + .slice() + .sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + + if (notifications.length < 1) { + return ( +
+

+ You dont have any notification yet. +

+
+ ); + } + + const goToLogin = () => { + window.location.href = "/login"; + }; + + if (!getCurrentUser) { + return ( +
+ +
+ ); + } + + return ( +
+
+ {sortedNotifications.map((notification, index) => ( + +
+ {notification.isRead ? ( + + ) : ( + + )} +
+

+ {notification.message} +

+

+ {formatDate(notification.createdAt)} +

+
+
+

+ View Detail +

+ + ))} +
+
+ ); +}; + +export default UserNotifications; diff --git a/src/components/common/user-notifications/UserNotificationDetail.tsx b/src/components/common/user-notifications/UserNotificationDetail.tsx new file mode 100644 index 0000000..cbef003 --- /dev/null +++ b/src/components/common/user-notifications/UserNotificationDetail.tsx @@ -0,0 +1,70 @@ +import React, { useEffect } from "react"; +import { useParams } from "react-router-dom"; +import { FaEnvelope, FaEnvelopeOpenText } from "react-icons/fa"; + +import { useAppDispatch, useAppSelector } from "../../../redux/hooks"; +import { readNotification } from "../../../redux/reducers/notificationSlice"; + +const UserNotificationDetail = () => { + const { id } = useParams<{ id: string }>(); + const { notifications } = useAppSelector((state) => state.notifications); + + const dispatch = useAppDispatch(); + + const notification = notifications.find( + // @ts-ignore + (notif) => notif.id === parseInt(id, 10), + ); + + if (!notification) { + return ( +
+

Notification not found!

+
+ ); + } + + const formatDate = (dateString: Date) => { + const date = new Date(dateString); + const options = { + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + }; + // @ts-ignore + return date.toLocaleDateString("en-US", options); + }; + useEffect(() => { + if (notification && !notification.isRead) { + dispatch(readNotification(notification.id)); + } + }, []); + + useEffect(() => { + if (id) { + dispatch(readNotification(Number(id))); + } + }, []); + + return ( +
+
+

+ When : + {formatDate(notification.createdAt)} +

+ +
+

+ {notification.message} +

+
+
+
+ ); +}; + +export default UserNotificationDetail; diff --git a/src/redux/reducers/notificationSlice.ts b/src/redux/reducers/notificationSlice.ts index f0e0d17..e11016b 100644 --- a/src/redux/reducers/notificationSlice.ts +++ b/src/redux/reducers/notificationSlice.ts @@ -1,6 +1,7 @@ import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import api from "../api/api"; +import { getCurrentUser } from "../../utils/currentuser"; export interface INotificationR { id: number; @@ -66,6 +67,7 @@ export const readNotification = createAsyncThunk( interface INotificationRootState { notifications: INotificationR[]; unreadCount: number; + currentUser: ICurrentUser | null; isConnected: boolean; } @@ -73,8 +75,21 @@ const initialState: INotificationRootState = { notifications: [], unreadCount: 0, isConnected: false, + currentUser: null, }; +export const handleCurrentUser = createAsyncThunk( + "user/currentUser", + async () => { + try { + const user = await getCurrentUser(); + return user; + } catch (error: any) { + return null; + } + }, +); + const notificationSlice = createSlice({ name: "notifications", initialState, @@ -102,15 +117,19 @@ const notificationSlice = createSlice({ }, }, extraReducers: (builder) => { - builder.addCase( - getUserNotifications.fulfilled, - (state, action: PayloadAction) => { - // state.notifications = action.payload; - // state.unreadCount = action.payload.filter( - // (notification) => !notification.isRead, - // ).length; - }, - ); + builder + .addCase( + getUserNotifications.fulfilled, + (state, action: PayloadAction) => { + // state.notifications = action.payload; + // state.unreadCount = action.payload.filter( + // (notification) => !notification.isRead, + // ).length; + }, + ) + .addCase(handleCurrentUser.fulfilled, (state, action) => { + state.currentUser = action.payload; + }); }, }); diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 7d68147..b371836 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -34,6 +34,8 @@ import SignupVerification from "../pages/SignupVerification"; import SmoothScroll from "../utils/SmoothScroll"; import NotFound from "../pages/NotFound"; import Payment, { SuccessfulPayment } from "../pages/paymentPage"; +import UserNotifications from "../components/common/user-notifications/UserNotifcations"; +import UserNotificationDetail from "../components/common/user-notifications/UserNotificationDetail"; const AppRoutes = () => { const navigate = useNavigate(); @@ -60,57 +62,62 @@ const AppRoutes = () => { return ( - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - } /> - } /> - } /> + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + + } /> + } /> + } /> + } /> + } /> - - - - )} - /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - } - /> - } /> - } /> - + + + + )} + /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } + /> + } /> + } /> + ); }; diff --git a/src/utils/currentuser.ts b/src/utils/currentuser.ts index b5fee7a..942e477 100644 --- a/src/utils/currentuser.ts +++ b/src/utils/currentuser.ts @@ -1,4 +1,5 @@ import api from "../redux/api/api"; +import { ICurrentUser } from "../redux/reducers/notificationSlice"; export const getCurrentUser = async () => { try { @@ -8,7 +9,7 @@ export const getCurrentUser = async () => { Authorization: `Bearer ${localStorage.getItem("accessToken")}`, }, }); - return response.data; + return response.data as ICurrentUser; } catch (error: any) { return null; }