diff --git a/jest.config.ts b/jest.config.ts index 0cbc147..13a1b81 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -8,5 +8,6 @@ export default { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)$": "/src/__test__/__mock__/fileMock.ts", "\\.(css|less)$": "identity-obj-proxy", + "~src/(.*)": "/src/$1", }, }; diff --git a/package-lock.json b/package-lock.json index 41239ca..17bc653 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "gsap": "^3.12.5", "install": "^0.13.0", "jest-environment-jsdom": "^29.7.0", - "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^3.0.7", "jwt-decode": "^4.0.0", "moment": "^2.30.1", @@ -62,6 +61,7 @@ "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", "@testing-library/dom": "^10.2.0", + "@testing-library/jest-dom": "^6.4.8", "@types/jest": "^29.5.12", "@types/jwt-decode": "^3.1.0", "@types/node": "^20.14.8", @@ -90,6 +90,7 @@ "husky": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", + "jest-fetch-mock": "^3.0.3", "lint-staged": "^15.2.5", "msw": "^2.3.1", "postcss": "^8.4.38", @@ -3766,9 +3767,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/@testing-library/jest-dom": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.6.tgz", - "integrity": "sha512-8qpnGVincVDLEcQXWaHOf6zmlbwTKc6Us6PPu4CRnPXCzo2OGBS5cwgMMOWdxDpEz1mkbvXHpEy99M5Yvt682w==", + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", + "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", "dev": true, "dependencies": { "@adobe/css-tools": "^4.4.0", @@ -3784,30 +3785,6 @@ "node": ">=14", "npm": ">=6", "yarn": ">=1" - }, - "peerDependencies": { - "@jest/globals": ">= 28", - "@types/bun": "latest", - "@types/jest": ">= 28", - "jest": ">= 28", - "vitest": ">= 0.32" - }, - "peerDependenciesMeta": { - "@jest/globals": { - "optional": true - }, - "@types/bun": { - "optional": true - }, - "@types/jest": { - "optional": true - }, - "jest": { - "optional": true - }, - "vitest": { - "optional": true - } } }, "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { @@ -5709,6 +5686,7 @@ "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, "dependencies": { "node-fetch": "^2.6.12" } @@ -5717,6 +5695,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -5735,17 +5714,20 @@ "node_modules/cross-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "node_modules/cross-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "node_modules/cross-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -9293,6 +9275,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, "dependencies": { "cross-fetch": "^3.0.4", "promise-polyfill": "^8.1.3" @@ -14053,7 +14036,8 @@ "node_modules/promise-polyfill": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", - "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==" + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true }, "node_modules/prompts": { "version": "2.4.2", diff --git a/package.json b/package.json index 01897b9..7b88bb6 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "gsap": "^3.12.5", "install": "^0.13.0", "jest-environment-jsdom": "^29.7.0", - "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^3.0.7", "jwt-decode": "^4.0.0", "moment": "^2.30.1", @@ -70,6 +69,7 @@ "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", "@testing-library/dom": "^10.2.0", + "@testing-library/jest-dom": "^6.4.8", "@types/jest": "^29.5.12", "@types/jwt-decode": "^3.1.0", "@types/node": "^20.14.8", @@ -98,6 +98,7 @@ "husky": "^8.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", + "jest-fetch-mock": "^3.0.3", "lint-staged": "^15.2.5", "msw": "^2.3.1", "postcss": "^8.4.38", diff --git a/src/App.tsx b/src/App.tsx index 4414b75..dcdb582 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"; @@ -29,14 +32,18 @@ const App: React.FC = () => { }; initializeSocket(); - dispatch(getUserNotifications()); return () => { disconnectFromSocket(); }; }, [dispatch]); + React.useEffect(() => { - dispatch(getUserNotifications()); + const token = localStorage.getItem("accessToken"); + if (token) { + dispatch(handleCurrentUser()); + dispatch(getUserNotifications()); + } }, [dispatch]); const { isPasswordExpired } = useAppSelector((state) => state.updatePin); diff --git a/src/__test__/currentUser.test.ts b/src/__test__/currentUser.test.ts new file mode 100644 index 0000000..a9e3b42 --- /dev/null +++ b/src/__test__/currentUser.test.ts @@ -0,0 +1,41 @@ +import api from "../redux/api/api"; +import { ICurrentUser } from "../redux/reducers/notificationSlice"; +import { getCurrentUser } from "../utils/currentuser"; + +jest.mock("../redux/api/api"); + +const mockUser: ICurrentUser = { + id: 1, + name: "John Doe", + username: "johndoe", + email: "john.doe@example.com", + password: "securepassword", + lastPasswordUpdateTime: new Date("2023-01-01T00:00:00Z"), + roleId: 2, + isActive: true, + isVerified: true, + createdAt: new Date("2022-01-01T00:00:00Z"), + updatedAt: new Date("2023-01-01T00:00:00Z"), +}; + +describe("getCurrentUser", () => { + beforeEach(() => { + (api.get as jest.Mock).mockReset(); + }); + + it("should return user data when API call is successful", async () => { + (api.get as jest.Mock).mockResolvedValue({ data: mockUser }); + + const result = await getCurrentUser(); + + expect(result).toEqual(mockUser); + }); + + it("should return null when API call fails", async () => { + (api.get as jest.Mock).mockRejectedValue(new Error("Network Error")); + + const result = await getCurrentUser(); + + expect(result).toBeNull(); + }); +}); diff --git a/src/__test__/notificationSlice.test.ts b/src/__test__/notificationSlice.test.ts index ce95b28..041cff5 100644 --- a/src/__test__/notificationSlice.test.ts +++ b/src/__test__/notificationSlice.test.ts @@ -87,7 +87,7 @@ describe("notificationSlice", () => { expect(state.unreadCount).toBe(1); }); - it.skip("should handle getUserNotifications thunk", async () => { + it("should handle getUserNotifications thunk", async () => { const mockNotifications: INotificationR[] = [ { id: 1, @@ -113,17 +113,13 @@ describe("notificationSlice", () => { data: { notifications: mockNotifications }, }); - (connectSocketMock as jest.Mock).mockReturnValue(socketMock); - - await store.dispatch(getUserNotifications()); + const result = await store.dispatch(getUserNotifications()); + console.log("Thunk result:", result); const state = store.getState().notifications; - console.log("State after getUserNotifications:", state); - // expect(state.notifications).toEqual(mockNotifications); - expect(state.unreadCount).toBe(1); - expect(socketMock.emit).not.toHaveBeenCalled(); - expect(socketMock.on).not.toHaveBeenCalled(); + expect(state.notifications).toEqual(mockNotifications); + expect(state.unreadCount).toBe(2); }); it("should handle readNotification thunk", async () => { diff --git a/src/__test__/userNotification.test.tsx b/src/__test__/userNotification.test.tsx new file mode 100644 index 0000000..6b2deca --- /dev/null +++ b/src/__test__/userNotification.test.tsx @@ -0,0 +1,209 @@ +import { render, screen } from "@testing-library/react"; +import { BrowserRouter as Router } from "react-router-dom"; +import { Provider } from "react-redux"; +import configureStore from "redux-mock-store"; + +import * as currentUserUtils from "../utils/currentuser"; +import { + ICurrentUser, + INotificationR, +} from "../redux/reducers/notificationSlice"; +import UserNotifications from "../components/common/user-notifications/UserNotifcations"; + +// Mock the react-icons +jest.mock("react-icons/fa", () => ({ + FaEnvelope: () =>
, + FaEnvelopeOpenText: () =>
, +})); + +const mockStore = configureStore([]); + +describe("UserNotifications", () => { + let store; + + beforeEach(() => { + store = mockStore({ + notifications: { + notifications: [] as INotificationR[], + currentUser: null as ICurrentUser | null, + }, + }); + }); + + it("renders no notifications message when there are no notifications", () => { + render( + + + + + , + ); + + expect(screen.queryByTestId("no-notifications")).toBeDefined(); + expect( + screen.queryByText("You dont have any notification yet."), + ).toBeDefined(); + }); + + it("renders login prompt when user is not logged in", () => { + // @ts-ignore + jest.spyOn(currentUserUtils, "getCurrentUser").mockReturnValue(null); + + render( + + + + + , + ); + + expect(screen.queryByTestId("login-prompt")).toBeDefined(); + expect(screen.queryByTestId("login-button")).toBeDefined(); + }); + + it("renders notifications list when there are notifications", () => { + const mockNotifications: INotificationR[] = [ + { + id: 1, + userId: 1, + title: "Notification 1", + message: "Test notification 1", + isRead: false, + createdAt: new Date("2023-01-01T12:00:00"), + updatedAt: new Date(), + }, + { + id: 2, + userId: 1, + title: "Notification 2", + message: "Test notification 2", + isRead: true, + createdAt: new Date("2023-01-02T12:00:00"), + updatedAt: new Date(), + }, + ]; + + store = mockStore({ + notifications: { + notifications: mockNotifications, + currentUser: { roleId: 1 } as ICurrentUser, + }, + }); + + render( + + + + + , + ); + + expect(screen.queryByTestId("notifications-list")).toBeDefined(); + expect(screen.queryAllByTestId(/^notification-/)).toHaveLength(6); + expect(screen.queryByTestId("unread-icon")).toBeDefined(); + expect(screen.queryByTestId("read-icon")).toBeDefined(); + }); + + it("sorts notifications by date in descending order", () => { + const mockNotifications: INotificationR[] = [ + { + id: 1, + userId: 1, + title: "Older notification", + message: "Older notification", + isRead: false, + createdAt: new Date("2023-01-01T12:00:00"), + updatedAt: new Date(), + }, + { + id: 2, + userId: 1, + title: "Newer notification", + message: "Newer notification", + isRead: true, + createdAt: new Date("2023-01-02T12:00:00"), + updatedAt: new Date(), + }, + ]; + + store = mockStore({ + notifications: { + notifications: mockNotifications, + currentUser: { roleId: 1 } as ICurrentUser, + }, + }); + + render( + + + + + , + ); + + expect(screen.queryByTestId("notification-message-2")).toBeDefined(); + expect(screen.queryByTestId("notification-message-1")).toBeDefined(); + }); + + it("renders correct link for regular user", () => { + const mockNotifications: INotificationR[] = [ + { + id: 1, + userId: 1, + title: "Test notification", + message: "Test notification", + isRead: false, + createdAt: new Date("2023-01-01T12:00:00"), + updatedAt: new Date(), + }, + ]; + + store = mockStore({ + notifications: { + notifications: mockNotifications, + currentUser: { roleId: 1 } as ICurrentUser, + }, + }); + + render( + + + + + , + ); + + expect(screen.queryByTestId("notification-1")).toBeDefined(); + }); + + it("renders correct link for admin user", () => { + const mockNotifications: INotificationR[] = [ + { + id: 1, + userId: 1, + title: "Test notification", + message: "Test notification", + isRead: false, + createdAt: new Date("2023-01-01T12:00:00"), + updatedAt: new Date(), + }, + ]; + + store = mockStore({ + notifications: { + notifications: mockNotifications, + currentUser: { roleId: 2 } as ICurrentUser, + }, + }); + + render( + + + + + , + ); + + expect(screen.queryByTestId("notification-1")).toBeDefined(); + }); +}); diff --git a/src/components/cards/Notification.tsx b/src/components/cards/Notification.tsx index 135eb4a..c48befb 100644 --- a/src/components/cards/Notification.tsx +++ b/src/components/cards/Notification.tsx @@ -17,7 +17,16 @@ export const NotificationPopup: React.FC = ({ handleClose, open, }) => { - const { notifications } = useAppSelector((state) => state.notifications); + const { notifications, currentUser } = useAppSelector( + (state) => state.notifications, + ); + + const recentNotifications = [...notifications] + .sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ) + .slice(0, 5); return ( = ({ }} > {notifications.length > 0 ? ( - notifications.slice(0, 5).map((notification, index) => ( + recentNotifications.map((notification, index) => ( <> {notification.isRead ? ( - + ) : ( - + )}

{notification.message}

@@ -88,7 +101,11 @@ export const NotificationPopup: React.FC = ({ )} 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..52c5436 --- /dev/null +++ b/src/components/common/user-notifications/UserNotifcations.tsx @@ -0,0 +1,128 @@ +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, currentUser } = 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..eeb2159 --- /dev/null +++ b/src/components/common/user-notifications/UserNotificationDetail.tsx @@ -0,0 +1,74 @@ +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, currentUser } = 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/pages/ProductPage.tsx b/src/pages/ProductPage.tsx index af02cfe..206fb16 100644 --- a/src/pages/ProductPage.tsx +++ b/src/pages/ProductPage.tsx @@ -91,7 +91,7 @@ const ProductPage = () => { const handleFilter = (filtered: IProduct[]) => { setFilteredProducts(filtered); - setCurrentPage(1); + // setCurrentPage(1); }; const indexOfLastItem = currentPage * itemsPerPage; @@ -109,10 +109,9 @@ const ProductPage = () => { }; const handleItemsPerPageChange = ( - event: React.ChangeEvent<{ value: unknown }>, + event: React.ChangeEvent, ) => { - setItemsPerPage(event.target.value as number); - setCurrentPage(1); + setItemsPerPage(Number(event.target.value)); }; if (loading) { diff --git a/src/redux/reducers/notificationSlice.ts b/src/redux/reducers/notificationSlice.ts index f0e0d17..9032b68 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 3c984d9..feb3318 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -33,6 +33,8 @@ import BuyerOrders from "../pages/BuyerOrders"; import SignupVerification from "../pages/SignupVerification"; import SmoothScroll from "../utils/SmoothScroll"; import NotFound from "../pages/NotFound"; +import UserNotifications from "../components/common/user-notifications/UserNotifcations"; +import UserNotificationDetail from "../components/common/user-notifications/UserNotificationDetail"; import { LogoutProvider } from "../components/dashboard/admin/LogoutContext"; import Payment, { CancelledPayment, @@ -72,7 +74,12 @@ const AppRoutes = () => { } /> } /> } /> - } /> + } /> + } /> + } + /> } /> } /> @@ -114,7 +121,7 @@ const AppRoutes = () => { } /> - + ); }; export default AppRoutes; 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; } diff --git a/src/utils/socket.ts b/src/utils/socket.ts index 796fdc4..5ed3897 100644 --- a/src/utils/socket.ts +++ b/src/utils/socket.ts @@ -25,9 +25,13 @@ export const connectToSocket = async () => { // alert("CONNECTED TO THE SERVER"); }); - const user = await getCurrentUser(); - if (user && user.id !== null) { - socket.emit("joinRoom", `${user.id}`); + const token = localStorage.getItem("accessToken"); + + if (token) { + const user = await getCurrentUser(); + if (user && user.id !== null) { + socket.emit("joinRoom", `${user.id}`); + } } socket.on("notification", (notification) => {