From aabeedbee5258490ece95040587fd866b4ae6acf Mon Sep 17 00:00:00 2001 From: patrickhag Date: Sun, 21 Jul 2024 21:15:50 +0200 Subject: [PATCH] Feature that allows seller to see statitics on the landing page of his/her dashboard. modified: package.json modified: src/App.tsx new file: src/assets/face-id-error.svg new file: src/components/dashboard/AreaChart.tsx new file: src/components/dashboard/BarChart.tsx modified: src/components/dashboard/Navbar.tsx new file: src/components/dashboard/StatisticsCard.tsx new file: src/components/dashboard/Table.tsx new file: src/components/dashboard/TopSellingProductCard.tsx new file: src/components/seller/DeleteProductModal.tsx new file: src/containers/Error/ErrorContainer.tsx modified: src/containers/herosection/HeroPage.tsx new file: src/pages/ErrorPage.tsx modified: src/pages/seller/AddNewProduct.tsx new file: src/pages/seller/EditProduct.tsx modified: src/pages/seller/Products.tsx modified: src/pages/seller/index.tsx modified: src/redux/slices/productsSlice.ts modified: src/services/productApi.ts modified: src/services/userApi.ts modified: src/types/Types.ts new file: vite.config.ts.timestamp-1721377722937-8c29150a52b3a.mjs --- package.json | 2 + src/App.tsx | 24 +- src/assets/face-id-error.svg | 10 + src/components/dashboard/AreaChart.tsx | 34 +++ src/components/dashboard/BarChart.tsx | 55 ++++ src/components/dashboard/Navbar.tsx | 2 +- src/components/dashboard/StatisticsCard.tsx | 18 ++ src/components/dashboard/Table.tsx | 121 +++++++++ .../dashboard/TopSellingProductCard.tsx | 21 ++ src/components/seller/DeleteProductModal.tsx | 79 ++++++ src/containers/Error/ErrorContainer.tsx | 36 +++ src/containers/herosection/HeroPage.tsx | 108 ++++---- src/pages/ErrorPage.tsx | 13 + src/pages/seller/AddNewProduct.tsx | 10 +- src/pages/seller/EditProduct.tsx | 237 ++++++++++++++++++ src/pages/seller/Products.tsx | 187 ++++++++++---- src/pages/seller/index.tsx | 46 +++- src/redux/slices/productsSlice.ts | 11 +- src/services/index.ts | 2 +- src/services/productApi.ts | 31 ++- src/services/userApi.ts | 4 +- src/types/Types.ts | 23 +- ....timestamp-1721377722937-8c29150a52b3a.mjs | 34 +++ 23 files changed, 960 insertions(+), 148 deletions(-) create mode 100644 src/assets/face-id-error.svg create mode 100644 src/components/dashboard/AreaChart.tsx create mode 100644 src/components/dashboard/BarChart.tsx create mode 100644 src/components/dashboard/StatisticsCard.tsx create mode 100644 src/components/dashboard/Table.tsx create mode 100644 src/components/dashboard/TopSellingProductCard.tsx create mode 100644 src/components/seller/DeleteProductModal.tsx create mode 100644 src/containers/Error/ErrorContainer.tsx create mode 100644 src/pages/ErrorPage.tsx create mode 100644 src/pages/seller/EditProduct.tsx create mode 100644 vite.config.ts.timestamp-1721377722937-8c29150a52b3a.mjs diff --git a/package.json b/package.json index e257bb0..b03b61b 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,12 @@ "@types/react-redux": "^7.1.33", "apexcharts": "^3.49.1", "axios": "^1.7.2", + "chart.js": "^4.4.3", "clsx": "^2.1.1", "jwt-decode": "^4.0.0", "react": "^18.2.0", "react-apexcharts": "^1.4.1", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.5", "react-icons": "^5.2.1", diff --git a/src/App.tsx b/src/App.tsx index 52c147e..8d9b87f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,9 +17,7 @@ import Category from './pages/admin/Category'; import Sellers from './pages/admin/Sellers'; import Buyers from './pages/admin/Buyers'; import UserManagement from './pages/admin/UserManagement'; -import NotFoundPage from './pages/NotFoundPage'; import SellerSettings from './pages/seller/Settings'; -import SellersPage from './pages/seller'; import Orders from './pages/seller/Orders'; import Products from './pages/seller/Products'; import Customers from './pages/seller/Customers'; @@ -37,22 +35,26 @@ import PaymentSuccessCard from './components/checkout/PaymentSuccessCard'; import PaymentPage from './pages/PaymentPage'; import { useSelector } from 'react-redux'; import { RootState } from './redux/store'; +import { SellerDashboard } from './pages/seller'; +import { ErrorPage } from './pages/ErrorPage'; +import EditProduct from './pages/seller/EditProduct'; import BuyerRestrictedRoutes from './containers/buyer/BuyerRestrictedRoutes'; const App = () => { const isAuthenticated = useSelector((state: RootState) => state.user.token); const dispatch = useDispatch(); useEffect(() => { - dispatch(productsApi.endpoints.getProducts.initiate()) + dispatch(productsApi.endpoints.getProducts.initiate()); if (isAuthenticated) { dispatch(cartApi.endpoints.getCarts.initiate()); - console.log("Cart") + console.log('Cart'); } }, [dispatch]); const router = createBrowserRouter([ { path: '/', + errorElement: , children: [ { index: true, @@ -109,7 +111,7 @@ const App = () => { { path: 'buyer-profile', element: , - } + }, ], }, { @@ -159,7 +161,7 @@ const App = () => { children: [ { index: true, - element: , + element: , }, { path: 'orders', @@ -170,9 +172,13 @@ const App = () => { element: , }, { - path: 'add-new-product', + path: 'products/add-new-product', element: , }, + { + path: 'products/edit-product/:id', + element: , + }, { path: 'customers', element: , @@ -187,10 +193,6 @@ const App = () => { path: 'search', element: , }, - { - path: '*', - element: , - }, ]); return ( <> diff --git a/src/assets/face-id-error.svg b/src/assets/face-id-error.svg new file mode 100644 index 0000000..03ec86d --- /dev/null +++ b/src/assets/face-id-error.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/components/dashboard/AreaChart.tsx b/src/components/dashboard/AreaChart.tsx new file mode 100644 index 0000000..6592fde --- /dev/null +++ b/src/components/dashboard/AreaChart.tsx @@ -0,0 +1,34 @@ +import React, { useEffect } from 'react'; +import { Line } from 'react-chartjs-2'; +import { Chart, ChartData, ChartOptions } from 'chart.js/auto'; + +const AreaChart: React.FC = () => { + const data: ChartData<'line'> = { + labels: ['Jan', 'Feb', 'Mar', 'April', 'May', 'June', 'July', 'August', 'Sept', 'Oct', 'Nov', 'Dec'], + datasets: [ + { + label: 'Yearly sales', + data: [200, 300, 1700, 400, 300, 200, 100, 100, 100, 50, 50], + borderColor: 'rgba(75, 192, 192, 1)', + backgroundColor: '#0E9CFF', + fill: true, + borderWidth: 2, + }, + ], + }; + + const options: ChartOptions<'line'> = { + responsive: true, + maintainAspectRatio: false, + }; + + useEffect(() => { + return () => { + Object.values(Chart.instances).forEach(instance => instance.destroy()); + }; + }, []); + + return ; +}; + +export default AreaChart; diff --git a/src/components/dashboard/BarChart.tsx b/src/components/dashboard/BarChart.tsx new file mode 100644 index 0000000..3f4ee1a --- /dev/null +++ b/src/components/dashboard/BarChart.tsx @@ -0,0 +1,55 @@ +// BarChart.tsx +import React from 'react'; +import { Bar } from 'react-chartjs-2'; +import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, ChartOptions } from 'chart.js'; +import { ChartData } from 'chart.js/auto'; + +ChartJS.register(CategoryScale, LinearScale, BarElement); + +const BarChart: React.FC = () => { + const data: ChartData<'bar'> = { + labels: ['', '', '', '', '', ''], + datasets: [ + { + data: [65, 59, 80, 81, 56, 55], + backgroundColor: '#1877F2', + borderRadius: 5, // No border radius + }, + ], + }; + + const options: ChartOptions<'bar'> = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: false, + }, + }, + scales: { + x: { + display: false, + grid: { + display: false, + }, + }, + y: { + display: false, + grid: { + display: false, + }, + }, + }, + }; + + return ( +
+ +
+ ); +}; + +export default BarChart; diff --git a/src/components/dashboard/Navbar.tsx b/src/components/dashboard/Navbar.tsx index cdf10ac..f67e94f 100644 --- a/src/components/dashboard/Navbar.tsx +++ b/src/components/dashboard/Navbar.tsx @@ -21,7 +21,7 @@ export default function Navbar({ location, page }: { location: string; page: str return (

{location}

diff --git a/src/components/dashboard/StatisticsCard.tsx b/src/components/dashboard/StatisticsCard.tsx new file mode 100644 index 0000000..28fa48b --- /dev/null +++ b/src/components/dashboard/StatisticsCard.tsx @@ -0,0 +1,18 @@ +import BarChart from './BarChart'; + +export const StatisticsCard = () => { + return ( +
+
+

New Products

+

2,300

+

+ 12.5% since last month +

+
+
+ +
+
+ ); +}; diff --git a/src/components/dashboard/Table.tsx b/src/components/dashboard/Table.tsx new file mode 100644 index 0000000..98e2d4e --- /dev/null +++ b/src/components/dashboard/Table.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { Transaction } from '../../types/Types'; + +const transactions: Transaction[] = [ + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'Delivered', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'Canceled', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'Pending', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'In review', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'Pending', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'In review', + price: '$100', + }, + { + reference: '18342342342343', + customer: 'John doe', + product: 'T-shirts', + date: '10 Mar, 4:32 am', + status: 'In review', + price: '$100', + }, +]; + +const statusColors: { [key: string]: string } = { + 'Delivered': 'bg-[#d1fae5] text-[#065f46]', + 'Canceled': 'bg-[#fee2e2] text-[#991b1b]', + 'Pending': 'bg-[#e9d5ff] text-[#6b21a8]', + 'In review': 'bg-[#fef3c7] text-[#854d0e]', +}; + +const TransactionTable: React.FC = () => { + return ( +
+
+
+

Transactions

+

List of latest transactions

+
+
+ + + + + + + + + + + + + {transactions.map((transaction, index) => ( + + + + + + + + + ))} + +
REFERENCE NUMBERCUSTOMERPRODUCTDATESTATUSPRICE
{transaction.reference}{transaction.customer}{transaction.product}{transaction.date} + + {transaction.status} + + {transaction.price}
+
+
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default TransactionTable; diff --git a/src/components/dashboard/TopSellingProductCard.tsx b/src/components/dashboard/TopSellingProductCard.tsx new file mode 100644 index 0000000..c223d79 --- /dev/null +++ b/src/components/dashboard/TopSellingProductCard.tsx @@ -0,0 +1,21 @@ +export const TopSellingProductCard = () => { + return ( +
+
+ +
+
+

iPhone 14 Pro

+

+ 12.5% vs last month +

+
+
+
+

$445,557

+
+ ); +}; diff --git a/src/components/seller/DeleteProductModal.tsx b/src/components/seller/DeleteProductModal.tsx new file mode 100644 index 0000000..5c194d7 --- /dev/null +++ b/src/components/seller/DeleteProductModal.tsx @@ -0,0 +1,79 @@ +import { FaTimes } from 'react-icons/fa'; +import { BsExclamationCircle } from 'react-icons/bs'; +import { useDeleteProductMutation } from '../../services/productApi'; +import { Bounce, toast } from 'react-toastify'; +import { useDispatch } from 'react-redux'; +import { setCrudProductBehaviour } from '../../redux/slices/productsSlice'; + +export default function DeleteProductModal({ + openDeleteModal, + handleOpenDeleteModal, + productId, +}: { + openDeleteModal: boolean; + productId: string; + handleOpenDeleteModal: () => void; +}) { + const dispatch = useDispatch(); + + const [deleteProduct, { isLoading }] = useDeleteProductMutation(); + + const handleDelete = async () => { + const response = await deleteProduct(productId).unwrap(); + if (response.ok) { + toast.success(response.message, { + position: 'bottom-left', + autoClose: 3000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: 'dark', + transition: Bounce, + }); + handleOpenDeleteModal(); + dispatch(setCrudProductBehaviour(true)); + } + }; + + return ( + <> +
+
+
+ +
+ +

Are you sure you want to delete this product?

+ + +
+
+
+
+ + ); +} diff --git a/src/containers/Error/ErrorContainer.tsx b/src/containers/Error/ErrorContainer.tsx new file mode 100644 index 0000000..0c5a863 --- /dev/null +++ b/src/containers/Error/ErrorContainer.tsx @@ -0,0 +1,36 @@ +import { isRouteErrorResponse, useNavigate, useRouteError } from 'react-router-dom'; +import ErrorImage from '../../assets/face-id-error.svg'; +import Button from '../../components/common/Button'; + +export const ErrorContainer = () => { + const navigate = useNavigate(); + const error: any = useRouteError(); + console.log(error); + return ( +
+
+
+ +
+

+ {isRouteErrorResponse(error) ? 'Ooops page not found...' : 'Unexpected Error occured...🥹'} +

+

+ We're very sorry, the page you have requested could not be found or the URL has been typed + incorrectly. +

+

+ Perhaps, you can click on the button below 👇 or search for the product in the search input above 👆 +

+
+ + + + +
+
+
+
+
+ ); +}; diff --git a/src/containers/herosection/HeroPage.tsx b/src/containers/herosection/HeroPage.tsx index 4a04e36..ca078e9 100644 --- a/src/containers/herosection/HeroPage.tsx +++ b/src/containers/herosection/HeroPage.tsx @@ -1,9 +1,9 @@ import { useState, useEffect } from 'react'; -import { AiOutlineDollarCircle } from "react-icons/ai"; -import { MdOutlinePeopleAlt } from "react-icons/md"; -import { TbTruck } from "react-icons/tb"; -import { MdOutlinePayment } from "react-icons/md"; -import { IoShieldCheckmarkOutline } from "react-icons/io5"; +import { AiOutlineDollarCircle } from 'react-icons/ai'; +import { MdOutlinePeopleAlt } from 'react-icons/md'; +import { TbTruck } from 'react-icons/tb'; +import { MdOutlinePayment } from 'react-icons/md'; +import { IoShieldCheckmarkOutline } from 'react-icons/io5'; import Category from '../categories/Category'; // import NewArrivals from '../Arrivals/NewArrivals'; import FeaturedProduct from '../FeaturedProducts/FeaturedProducts'; @@ -13,7 +13,6 @@ import { RootState } from '../../redux/store'; import { setIsCategoriesFetched, setallCategories } from '../../redux/slices/categorySlice'; import CategorySkeleton from '../../components/categories/CategoriesSkeleton'; - function HeroPage() { const dispatch = useDispatch(); const { allCategories, isCategoriesFetched }: any = useSelector((state: RootState) => state.category); @@ -27,109 +26,114 @@ function HeroPage() { }, [categoriess, isCategoriesFetched, dispatch]); const categoriesToDisplay = allCategories.length ? allCategories.data : categoriess; - const list = categoriesToDisplay?.data + const list = categoriesToDisplay?.data; const images = [ - "https://images.unsplash.com/photo-1491553895911-0055eca6402d?q=80&w=1760&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", - "https://noticiasconcursos.com.br/wp-content/uploads/2023/08/noticiasconcursos.com.br-novo-iphone-da-apple-ja-tem-data-prevista-para-lancamento-veja-quando-09-iphone-15-750x430.jpg", - "https://media.istockphoto.com/id/1170348086/photo/black-headphones-isolated-on-white-background-flat-lay-top-view-copy-space-music-concept.webp?s=170667a&w=0&k=20&c=7laDBNwNFtg0_BDSy2coo8oZMyYK9TofQzZBa-nuG3c=" + 'https://images.unsplash.com/photo-1491553895911-0055eca6402d?q=80&w=1760&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + 'https://noticiasconcursos.com.br/wp-content/uploads/2023/08/noticiasconcursos.com.br-novo-iphone-da-apple-ja-tem-data-prevista-para-lancamento-veja-quando-09-iphone-15-750x430.jpg', + 'https://media.istockphoto.com/id/1170348086/photo/black-headphones-isolated-on-white-background-flat-lay-top-view-copy-space-music-concept.webp?s=170667a&w=0&k=20&c=7laDBNwNFtg0_BDSy2coo8oZMyYK9TofQzZBa-nuG3c=', ]; // hero images auto display const [currentImageIndex, setCurrentImageIndex] = useState(0); useEffect(() => { const interval = setInterval(() => { - setCurrentImageIndex((prevIndex) => (prevIndex === images.length - 1 ? 0 : prevIndex + 1)); + setCurrentImageIndex(prevIndex => (prevIndex === images.length - 1 ? 0 : prevIndex + 1)); }, 5000); return () => clearInterval(interval); }, []); - return ( -
-
-
-
+
+
+
+
Hot Phones -

Hot Sells from new Arrivals

+

+ Hot Sells from new Arrivals +

-
+
Best Shoes in Town -

Enjoy Black Friday Discount

+

+ Enjoy Black Friday Discount +

-
+
{images.map((src, index) => ( {`Sliding ))} -

+

-
+

Shop By Category Here:

-
+
{list && list.length ? ( -
- {list.map((cat: { image: string, name: string }, index: number) => ( -
+
+ {list.map((cat: { image: string; name: string }, index: number) => ( +
))}
) : ( -
+
)}
-
-
SMARTER VALUES, GREAT DEALS
-
+
+
+ SMARTER VALUES, GREAT DEALS +
+
-
+

Value-For-Money

-

We offer compentitive price over millions of items

+

We offer compentitive price over millions of items

-
+
-
+

Shoppers worldwide

-

More than 300 millions shoppers form 200+ countries & region

+

More than 300 millions shoppers form 200+ countries & region

-
+
-
+

Fast delivery

-

Faster delivery on selected items. Thanks to our improved logistics

+

Faster delivery on selected items. Thanks to our improved logistics

-
+
-
+

Safe payments

-

Safe payment methods preferred by international shoppers

+

Safe payment methods preferred by international shoppers

-
+
-
+

Buyer protection

-

Get refund if item arrived late or not as described

+

Get refund if item arrived late or not as described

@@ -138,7 +142,7 @@ function HeroPage() {
*/} {/* Featured Products */} -
+
diff --git a/src/pages/ErrorPage.tsx b/src/pages/ErrorPage.tsx new file mode 100644 index 0000000..6356c1a --- /dev/null +++ b/src/pages/ErrorPage.tsx @@ -0,0 +1,13 @@ +import Footer from '../components/footer/Footer'; +import Navbar from '../components/navbar/Navbar'; +import { ErrorContainer } from '../containers/Error/ErrorContainer'; + +export const ErrorPage = () => { + return ( + <> + + +