From 4b6e98c7db1f0eb880372fff0467ed2af0b51bf5 Mon Sep 17 00:00:00 2001 From: tchami <131756888+tchamianest@users.noreply.github.com> Date: Thu, 27 Jun 2024 23:26:03 +0200 Subject: [PATCH] Feat(Dashboard): The seller and admin dashboard --- package-lock.json | 10 ++ package.json | 9 +- public/dash1.png | Bin 0 -> 1002 bytes public/dash2.png | Bin 0 -> 1008 bytes public/orderdash.png | Bin 0 -> 975 bytes public/orderdash1.png | Bin 0 -> 2212 bytes public/wishdash.png | Bin 0 -> 251 bytes src/app/admin/categories/page.tsx | 40 ++++++ src/app/admin/home/page.tsx | 89 ++++++++++++ src/app/admin/product/page.tsx | 45 ++++++ src/app/admin/productcreate/page.tsx | 57 ++++++++ src/app/admin/profile/page.tsx | 40 ++++++ src/app/admin/users/page.tsx | 32 +++++ src/components/2faVerification.tsx | 10 +- src/components/AddProducts.tsx | 138 ++++++++++++------ src/components/BuyerOrdersList.tsx | 2 +- src/components/DashNavbar.tsx | 156 +++++++++++++++++++++ src/components/DashboardHeader.tsx | 62 ++++---- src/components/Header.tsx | 3 +- src/components/SellerSummary.tsx | 85 +++++++++++ src/components/Table.tsx | 78 ++++++----- src/components/chartssection.tsx | 113 +++++++++++++++ src/components/headerDash.tsx | 55 ++++++++ src/components/shortCard.tsx | 41 ++++++ src/hooks/useLogin.ts | 10 +- src/redux/slices/2faAuthenticationSlice.ts | 4 + 26 files changed, 955 insertions(+), 124 deletions(-) create mode 100644 public/dash1.png create mode 100644 public/dash2.png create mode 100644 public/orderdash.png create mode 100644 public/orderdash1.png create mode 100644 public/wishdash.png create mode 100644 src/app/admin/categories/page.tsx create mode 100644 src/app/admin/home/page.tsx create mode 100644 src/app/admin/product/page.tsx create mode 100644 src/app/admin/productcreate/page.tsx create mode 100644 src/app/admin/profile/page.tsx create mode 100644 src/app/admin/users/page.tsx create mode 100644 src/components/DashNavbar.tsx create mode 100644 src/components/SellerSummary.tsx create mode 100644 src/components/chartssection.tsx create mode 100644 src/components/headerDash.tsx create mode 100644 src/components/shortCard.tsx diff --git a/package-lock.json b/package-lock.json index e605161..00afeec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "npx": "^10.2.2", "react": "^18", "react-dom": "^18", + "react-google-charts": "^4.0.1", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", "react-query": "^3.39.3", @@ -27770,6 +27771,15 @@ "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", "dev": true }, + "node_modules/react-google-charts": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-google-charts/-/react-google-charts-4.0.1.tgz", + "integrity": "sha512-V/hcMcNuBgD5w49BYTUDye+bUKaPmsU5vy/9W/Nj2xEeGn+6/AuH9IvBkbDcNBsY00cV9OeexdmgfI5RFHgsXQ==", + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, "node_modules/react-hook-form": { "version": "7.52.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", diff --git a/package.json b/package.json index 4b66244..277cd45 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev ", "build": "next build", "start": "next start", "lint": "next lint", @@ -29,6 +29,7 @@ "npx": "^10.2.2", "react": "^18", "react-dom": "^18", + "react-google-charts": "^4.0.1", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", "react-query": "^3.39.3", @@ -36,8 +37,8 @@ "react-rating-stars-component": "^2.2.0", "react-redux": "^9.1.2", "react-router-dom": "^6.23.1", - "react-toastify": "^10.0.5", "react-slick": "^0.30.2", + "react-toastify": "^10.0.5", "redux-mock-store": "^1.5.4", "redux-thunk": "^3.1.0", "slick-carousel": "^1.8.1", @@ -97,8 +98,8 @@ "jest-dom": "^4.0.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.4", - "msw": "^2.3.1", "match-media-mock": "^0.1.1", + "msw": "^2.3.1", "postcss": "^8", "prettier": "^3.2.5", "react-test-renderer": "^18.3.1", @@ -109,4 +110,4 @@ "ts-node": "^10.9.2", "typescript": "^5.4.5" } -} \ No newline at end of file +} diff --git a/public/dash1.png b/public/dash1.png new file mode 100644 index 0000000000000000000000000000000000000000..f5aee4f80d7e8a9b2da75107abbdbebfea9ababb GIT binary patch literal 1002 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 + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = JSON.parse(user); + + setUser(finalUser.User); + }; + categ(); + }, []); + + if (!user) { + return ( +
+
+
+ ); + } + return ( +
+
+ +
+
+
+ +
+
+
+ ); +} + +export default page; diff --git a/src/app/admin/home/page.tsx b/src/app/admin/home/page.tsx new file mode 100644 index 0000000..088ad07 --- /dev/null +++ b/src/app/admin/home/page.tsx @@ -0,0 +1,89 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import DashNavbar from '@/components/DashNavbar'; +import HeaderDash from '@/components/headerDash'; +import { unstable_noStore as noStore } from 'next/cache'; +import SellerSummary from '@/components/SellerSummary'; +import Chartssection from '@/components/chartssection'; +import request from '@/utils/axios'; +import { useQuery } from '@tanstack/react-query'; + +function page() { + const [categories, setcategoris] = useState([]); + const [user, setUser] = useState(); + const [users, setUsers] = useState(); + + const { data, isLoading, error } = useQuery({ + queryKey: ['data'], + queryFn: async () => { + noStore(); + let data; + const date = new Date(); + const dataDateNow = date + .toLocaleDateString() + .replaceAll('/', '-') + .split('-'); + + const response: any = await request.get( + `http://localhost:5500/api/stats?start=2023-02-06&end=${dataDateNow[2]}-${dataDateNow[0].length > 1 ? dataDateNow[0] : '0' + dataDateNow[0]}-${dataDateNow[1]}`, + ); + data = response.data; + + return data; + }, + }); + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = JSON.parse(user); + + setUser(finalUser.User); + if (finalUser.User.Role.name === 'admin') { + const responseUsers: any = await request.get('/users'); + setUsers(responseUsers.users); + } + setcategoris(responsecat.categories); + }; + categ(); + }, []); + if (isLoading) { + return ( +
+
+
+ ); + } + return ( +
+
+ +
+
+
+ +
+ {/* body section */} +
+ {/* Statistic section */} +
+ + +
+
+
+
+ ); +} + +export default page; diff --git a/src/app/admin/product/page.tsx b/src/app/admin/product/page.tsx new file mode 100644 index 0000000..c4ca2a3 --- /dev/null +++ b/src/app/admin/product/page.tsx @@ -0,0 +1,45 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import DashNavbar from '@/components/DashNavbar'; +import HeaderDash from '@/components/headerDash'; +import request from '@/utils/axios'; +import ProductsTable from '@/components/Table'; + +function page() { + const [user, setUser] = useState(null); + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = JSON.parse(user); + + setUser(finalUser.User); + }; + categ(); + }, []); + + if (!user) { + return ( +
+
+
+ ); + } + return ( +
+
+ +
+
+
+ +
+
+ +
+
+
+ ); +} + +export default page; diff --git a/src/app/admin/productcreate/page.tsx b/src/app/admin/productcreate/page.tsx new file mode 100644 index 0000000..6e9ef80 --- /dev/null +++ b/src/app/admin/productcreate/page.tsx @@ -0,0 +1,57 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import DashNavbar from '@/components/DashNavbar'; +import HeaderDash from '@/components/headerDash'; +import request from '@/utils/axios'; +import ProductPopup from '@/components/AddProducts'; +import { useRouter } from 'next/navigation'; + +function page() { + const [user, setUser] = useState(); + const router = useRouter(); + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = JSON.parse(user); + + setUser(finalUser.User); + if (finalUser?.User.Role.name !== 'seller') { + router.push('/'); + } + }; + categ(); + }, []); + + if (!user) { + return ( +
+
+
+ ); + } + return ( +
+
+ +
+
+
+ +
+ {/* body section */} +
+ {/* Statistic section */} +
+ console.log('nothing')} + /> +
+
+
+
+ ); +} + +export default page; diff --git a/src/app/admin/profile/page.tsx b/src/app/admin/profile/page.tsx new file mode 100644 index 0000000..1dd0b09 --- /dev/null +++ b/src/app/admin/profile/page.tsx @@ -0,0 +1,40 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import DashNavbar from '@/components/DashNavbar'; +import HeaderDash from '@/components/headerDash'; +import request from '@/utils/axios'; + +function page() { + const [user, setUser] = useState(); + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = await JSON.parse(user); + + setUser(finalUser.User); + }; + categ(); + }, []); + if (!user) { + return ( +
+
+
+ ); + } + return ( +
+
+ +
+
+
+ +
+
+
+ ); +} + +export default page; diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx new file mode 100644 index 0000000..ba7593c --- /dev/null +++ b/src/app/admin/users/page.tsx @@ -0,0 +1,32 @@ +'use client'; +import React, { use, useEffect, useState } from 'react'; +import DashNavbar from '@/components/DashNavbar'; +import HeaderDash from '@/components/headerDash'; +import request from '@/utils/axios'; + +function page() { + const [user, setUser] = useState(); + useEffect(() => { + const categ = async () => { + const responsecat: any = await request.get('/categories'); + const user = localStorage.getItem('profile') || 'no'; + const finalUser = JSON.parse(user); + + setUser(finalUser.User); + }; + }, []); + return ( +
+
+ +
+
+
+ +
+
+
+ ); +} + +export default page; diff --git a/src/components/2faVerification.tsx b/src/components/2faVerification.tsx index 974d755..ab2b330 100644 --- a/src/components/2faVerification.tsx +++ b/src/components/2faVerification.tsx @@ -13,7 +13,6 @@ import { resendOTPCode, } from '@/redux/slices/2faAuthenticationSlice'; import GlobarPopUp from './UsablePopUp'; -import request from '@/utils/axios'; interface OtpVerifyInterface { isOpen: boolean; @@ -32,7 +31,7 @@ const OtpVerify: React.FC = ({ isOpen }) => { useEffect(() => { if (isAuthenticated) { - router.push('/seller/dashboard/products'); + router.push('/admin/home'); } }, [isAuthenticated]); @@ -45,12 +44,7 @@ const OtpVerify: React.FC = ({ isOpen }) => { const VerifyOtp = async () => { var otp = input.join(''); const result = otpValidation.safeParse({ otp }); - const result1 = await dispatch(handleOTPVerification(otp)); - if (result1) { - const profile = await request.get(`${URL}/users/profile`); - const userData = JSON.stringify(profile); - localStorage.setItem('profile', userData); - } + await dispatch(handleOTPVerification(otp)); }; const HandleInput = ( index: number, diff --git a/src/components/AddProducts.tsx b/src/components/AddProducts.tsx index f4fadbf..343c12c 100644 --- a/src/components/AddProducts.tsx +++ b/src/components/AddProducts.tsx @@ -1,4 +1,4 @@ -"use client"; +'use client'; import React, { useState, useEffect } from 'react'; import { useForm, SubmitHandler } from 'react-hook-form'; @@ -7,7 +7,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { createProduct, fetchCategories } from '../redux/slices/productSlice'; import { unwrapResult } from '@reduxjs/toolkit'; import type { AppDispatch, RootState } from '../redux/store'; -import { productSchema } from "../validations/productValidation"; +import { productSchema } from '../validations/productValidation'; import { showToast } from '@/helpers/toast'; import InputBox from './InputBox'; @@ -31,22 +31,33 @@ interface ProductPopupProps { onClose: () => void; } -const ProductPopup: React.FC = ({ isOpen, onClose }) => { +const ProductPopup: React.FC = ({ + isOpen = true, + onClose, +}) => { const dispatch = useDispatch(); - const { register, handleSubmit, setValue, formState: { errors }, getValues, trigger } = useForm({ + const { + register, + handleSubmit, + setValue, + formState: { errors }, + getValues, + trigger, + } = useForm({ resolver: zodResolver(productSchema), }); const [pictures, setPictures] = useState([]); const [files, setFiles] = useState([]); const [uploadError, setUploadError] = useState(null); - const { categories, status } = useSelector((state: RootState) => state.productsAddReducers); + const { categories, status } = useSelector( + (state: RootState) => state.productsAddReducers, + ); const [loading, setLoading] = useState(false); useEffect(() => { - if (isOpen) { - dispatch(fetchCategories()); - } - }, [dispatch, isOpen]); + const data = dispatch(fetchCategories()); + console.log(data); + }, [dispatch]); useEffect(() => { document.body.style.overflow = 'hidden'; @@ -55,8 +66,8 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { }; }, []); - const onSubmit: SubmitHandler = async data => { - console.log("data received", data); + const onSubmit: SubmitHandler = async (data) => { + console.log('data received', data); if (files.length < 4) { setUploadError('You must upload at least 4 pictures.'); return; @@ -73,7 +84,7 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { try { const resultAction = await dispatch(createProduct(data as IProduct)); const result = unwrapResult(resultAction); - showToast(result.message, 'success'); + showToast(result.message, 'success'); console.log(result); console.log(result.message); onClose(); @@ -93,8 +104,8 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { } else if (error.message) { errorMessage = error.message; } - - showToast(errorMessage, 'error'); + + showToast(errorMessage, 'error'); } finally { setLoading(false); } @@ -104,34 +115,37 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { if (e.target.files && e.target.files.length === 1) { const file = e.target.files[0]; const totalFiles = files.length + 1; - + // Check for maximum file limit if (totalFiles > 8) { setUploadError('You can upload a maximum of 8 pictures.'); return; } - + // Validate file type const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']; if (!allowedTypes.includes(file.type)) { setUploadError('Only jpeg, jpg, and png files are allowed.'); return; } - + // Validate file size (maximum size of 1MB) const maxSizeInBytes = 1 * 1024 * 1024; if (file.size > maxSizeInBytes) { setUploadError('Image size must be less than 1MB.'); return; } - + const filePreview = URL.createObjectURL(file); - setPictures(prevPictures => [...prevPictures, filePreview]); - setFiles(prevFiles => [...prevFiles, file]); - setValue('productPictures', [...getValues('productPictures') || [], file]); + setPictures((prevPictures) => [...prevPictures, filePreview]); + setFiles((prevFiles) => [...prevFiles, file]); + setValue('productPictures', [ + ...(getValues('productPictures') || []), + file, + ]); trigger('productPictures'); setUploadError(null); - + const updatedFiles = getValues('productPictures') || []; if (updatedFiles.length < 4) { setUploadError('You must upload at least 4 pictures.'); @@ -142,21 +156,21 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { setUploadError('Please upload one picture at a time.'); } }; - + const handleDeletePicture = (index: number) => { - setPictures(prevPictures => prevPictures.filter((_, i) => i !== index)); + setPictures((prevPictures) => prevPictures.filter((_, i) => i !== index)); const updatedFiles = files.filter((_, i) => i !== index); setFiles(updatedFiles); setValue('productPictures', updatedFiles); trigger('productPictures'); - + if (updatedFiles.length < 4) { setUploadError('You must upload at least 4 pictures.'); } else { setUploadError(null); } }; - + const getCurrentDate = () => { const today = new Date(); const year = today.getFullYear(); @@ -165,19 +179,25 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { return `${year}-${month}-${day}`; }; - if (!isOpen) return null; + // if (!isOpen) return null; return ( -
-
e.stopPropagation()}> +
+
e.stopPropagation()} + > -

Add New Product

+

Add New Product

= ({ isOpen, onClose }) => { error={errors.productName?.message} />
-
@@ -253,7 +282,10 @@ const ProductPopup: React.FC = ({ isOpen, onClose }) => { error={errors.stockLevel?.message} />
-
{pictures.map((picture, index) => (
- {`Preview + {`Preview