diff --git a/src/App.tsx b/src/App.tsx index 19593e4..f6ff5f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -25,6 +25,7 @@ import ResetPassword from './pages/ResetPassword'; import NewPassword from './pages/NewPassword'; import { ProductDetail } from './pages/product/ProductDetail'; +import BuyerProfile from './pages/BuyerProfile'; const App = () => { const { data, error, isLoading } = useGetProductsQuery(); const dispatch = useDispatch(); @@ -88,6 +89,10 @@ const App = () => { path: 'products/:id', element: , }, + { + path: 'buyer-profile', + element: , + } ], }, { diff --git a/src/containers/buyer/Account.tsx b/src/containers/buyer/Account.tsx new file mode 100644 index 0000000..1040834 --- /dev/null +++ b/src/containers/buyer/Account.tsx @@ -0,0 +1,78 @@ +// components/menu/Menu1.jsx +import { FaUserEdit, FaHourglassHalf } from "react-icons/fa"; +import { FaTruckFast } from "react-icons/fa6"; +import { MdOutlineFeedback } from "react-icons/md"; +import { PiKeyReturn } from "react-icons/pi"; +import { useDispatch } from "react-redux"; +import { setActiveMenu } from "../../redux/slices/buyerDashboard"; + + +const image: string = 'https://images.unsplash.com/photo-1533636721434-0e2d61030955?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D' + +const Account = () => { + const dispatch = useDispatch(); + return ( +
+ {/* User Profile Section */} +
+
+ Buyer Profile +
+

NP Leon

+

Buyer

+
+
+ dispatch(setActiveMenu("profile"))}/> +
+ + {/* Cards Section */} +
+ {/* Pending Card */} +
dispatch(setActiveMenu("pendingpayments"))}> +
+ 5 +
+ +
+

Pending Payments

+
+
+ + {/* All Card */} +
dispatch(setActiveMenu("intransit"))} + > +
+ 5 +
+ +
+

In Transit

+
+
+ + {/* Feedback Card */} +
dispatch(setActiveMenu("feedback"))} + > + +
+

Feedback

+
+
+ + {/* Refund & Return Card */} +
dispatch(setActiveMenu("returnrefund"))} + > + +
+

Return & Refund

+
+
+
+
+ ); +}; + +export default Account; diff --git a/src/containers/buyer/FeedBack.tsx b/src/containers/buyer/FeedBack.tsx new file mode 100644 index 0000000..2ebf9fa --- /dev/null +++ b/src/containers/buyer/FeedBack.tsx @@ -0,0 +1,153 @@ +import React, { useState } from 'react'; +import FeedbackModal from './FeedbackModal'; + +type Rating = number; + +interface Product { + id: string; + name: string; + price: string; + manufacturer: string; + ratings: Rating[]; +} + +const productsData: Product[] = [ + { + id: '1', + name: 'Product 1', + price: '$100', + manufacturer: 'Manufacturer A', + ratings: [], + }, + { + id: '2', + name: 'Product 2', + price: '$200', + manufacturer: 'Manufacturer B', + ratings: [4, 5], + }, + { + id: '3', + name: 'Product 3', + price: '$300', + manufacturer: 'Manufacturer C', + ratings: [3], + }, +]; + +const FeedBack: React.FC = () => { + const [products, setProducts] = useState(productsData); + const [modalOpen, setModalOpen] = useState(false); + const [currentProduct, setCurrentProduct] = useState(null); + const [currentPage, setCurrentPage] = useState(1); + const productsPerPage = 2; + + const indexOfLastProduct = currentPage * productsPerPage; + const indexOfFirstProduct = indexOfLastProduct - productsPerPage; + const currentProducts = products.slice(indexOfFirstProduct, indexOfLastProduct); + + const totalPages = Math.ceil(products.length / productsPerPage); + + const paginate = (pageNumber: number) => setCurrentPage(pageNumber); + + const calculateAverageRating = (ratings: Rating[]): string => { + if (ratings.length === 0) return 'No ratings'; + const sum = ratings.reduce((a, b) => a + b, 0); + return (sum / ratings.length).toFixed(1); + }; + + const handleAddFeedback = (product: Product) => { + setCurrentProduct(product); + setModalOpen(true); + }; + + const handleModalClose = () => { + setModalOpen(false); + setCurrentProduct(null); + }; + + const handleFeedbackSubmit = (feedback: { message: string; image: File | null; rating: number }) => { + console.log('Feedback submitted for product:', currentProduct?.id); + console.log('Feedback details:', feedback); + }; + + const handleDeleteFeedback = (productId: string, feedbackIndex: number) => { + setProducts((prevProducts) => + prevProducts.map((product) => + product.id === productId + ? { + ...product, + ratings: product.ratings.filter((_, index) => index !== feedbackIndex), + } + : product + ) + ); + }; + + return ( +
+

Product List

+ {currentProducts.map((product) => ( +
+
+
+

{product.name}

+

Average Rating: {calculateAverageRating(product.ratings)}

+
+
+ {product.ratings.length === 0 ? ( + + ) : ( +
+ {product.ratings.map((rating, index) => ( +
+

+ Feedback {index + 1}: {rating} stars +

+ +
+ ))} +
+ )} +
+
+
+ ))} +
+ {Array.from({ length: totalPages }, (_, index) => ( + + ))} +
+ {currentProduct && ( + + )} +
+ ); +}; + +export default FeedBack; diff --git a/src/containers/buyer/FeedbackModal.tsx b/src/containers/buyer/FeedbackModal.tsx new file mode 100644 index 0000000..8412cce --- /dev/null +++ b/src/containers/buyer/FeedbackModal.tsx @@ -0,0 +1,98 @@ +import React, { useState } from 'react'; + +interface FeedbackModalProps { + productName: string; + isOpen: boolean; + onClose: () => void; + onSubmit: (feedback: { message: string; image: File | null; rating: number }) => void; +} + +const FeedbackModal: React.FC = ({ productName, isOpen, onClose, onSubmit }) => { + const [message, setMessage] = useState(''); + const [image, setImage] = useState(null); + const [rating, setRating] = useState(0); + + const handleImageChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + setImage(e.target.files[0]); + } + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + onSubmit({ message, image, rating }); + onClose(); + }; + + if (!isOpen) return null; + + return ( +
+
+

Add Feedback for {productName}

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ ); +}; + +export default FeedbackModal; diff --git a/src/containers/buyer/InTransit.tsx b/src/containers/buyer/InTransit.tsx new file mode 100644 index 0000000..28c5839 --- /dev/null +++ b/src/containers/buyer/InTransit.tsx @@ -0,0 +1,113 @@ +import { useState } from "react"; +import { useDispatch } from "react-redux"; +import { setActiveMenu } from "../../redux/slices/buyerDashboard"; + +function InTransitOrders() { + const dispatch = useDispatch(); + + // Dummy orders data + const orders = [ + { + id: "764376473647634736", + status: "Pending", + date: "07/14/2024", + totalPrice: "$566", + }, + { + id: "998877665544332211", + status: "Processing", + date: "07/15/2024", + totalPrice: "$780", + }, + { + id: "554433221199887766", + status: "Pending", + date: "07/16/2024", + totalPrice: "$350", + }, + { + id: "332211998877665544", + status: "Shipped", + date: "07/17/2024", + totalPrice: "$450", + }, + { + id: "123456789012345678", + status: "Pending", + date: "07/18/2024", + totalPrice: "$1200", + }, + { + id: "112233445566778899", + status: "Shipped", + date: "07/19/2024", + totalPrice: "$980", + }, + { + id: "223344556677889900", + status: "Processing", + date: "07/20/2024", + totalPrice: "$610", + }, + ]; + + // Filter orders to only include those with status "Shipped" or "Processing" + const inTransitOrders = orders.filter(order => order.status === "Shipped" || order.status === "Processing"); + + const [currentPage, setCurrentPage] = useState(1); + const ordersPerPage = 3; + + const indexOfLastOrder = currentPage * ordersPerPage; + const indexOfFirstOrder = indexOfLastOrder - ordersPerPage; + const currentOrders = inTransitOrders.slice(indexOfFirstOrder, indexOfLastOrder); + + const totalPages = Math.ceil(inTransitOrders.length / ordersPerPage); + + const paginate = (pageNumber:any) => setCurrentPage(pageNumber); + + return ( +
+

In Transit Orders

+
+ {currentOrders.map((order) => ( +
+
+
+

Id:

+

{order.id}

+
+
+

Status:

+

{order.status}

+
+
+
+

{order.date}

+
+

Total Price:

+

{order.totalPrice}

+
+ +
+
+ ))} +
+
+ {Array.from({ length: totalPages }, (_, index) => ( + + ))} +
+
+ ); +} + +export default InTransitOrders; diff --git a/src/containers/buyer/OderDetails.tsx b/src/containers/buyer/OderDetails.tsx new file mode 100644 index 0000000..9d34bb6 --- /dev/null +++ b/src/containers/buyer/OderDetails.tsx @@ -0,0 +1,90 @@ +function OrderDetails() { + return ( +
+

Order Details

+
+ {/* Order Information */} +
+
+
+ Order ID: + 764376473647634736 +
+
+ Status: + Pending +
+
+
+
+ Date: + 03/24/2024 +
+
+ Total Price: + $566 +
+
+
+ + {/* Order Items */} +
+

Items

+
+
+
+ Item + Product Name +
+
+ Quantity: + 2 +
+
+ Price: + $150 +
+
+ + {/* Repeat for more items */} +
+
+ Item + Another Product +
+
+ Quantity: + 1 +
+
+ Price: + $266 +
+
+
+
+ + {/* Actions */} +
+ + +
+
+
+ ); + } + + export default OrderDetails; + \ No newline at end of file diff --git a/src/containers/buyer/Orders.tsx b/src/containers/buyer/Orders.tsx new file mode 100644 index 0000000..8cccce6 --- /dev/null +++ b/src/containers/buyer/Orders.tsx @@ -0,0 +1,118 @@ +import { useState } from "react"; +import { useDispatch } from "react-redux"; +import { setActiveMenu } from "../../redux/slices/buyerDashboard"; + +function Orders() { + const dispatch = useDispatch(); + + const orders = [ + { + id: "764376473647634736", + status: "Pending", + date: "34/67/2024", + totalPrice: "$566", + }, + { + id: "123456789012345678", + status: "Completed", + date: "12/01/2024", + totalPrice: "$1200", + }, + { + id: "987654321098765432", + status: "Cancelled", + date: "23/03/2024", + totalPrice: "$450", + }, + { + id: "112233445566778899", + status: "Shipped", + date: "15/05/2024", + totalPrice: "$780", + }, + { + id: "998877665544332211", + status: "Processing", + date: "29/07/2024", + totalPrice: "$350", + }, + ]; + + const [currentPage, setCurrentPage] = useState(1); + const [filter, setFilter] = useState("All"); + + const ordersPerPage = 3; + + const handleFilterChange = (e:any) => { + setFilter(e.target.value); + setCurrentPage(1); // Reset to first page when filter changes + }; + + const filteredOrders = orders.filter((order) => { + if (filter === "All") return true; + if (filter === "Pending Payments") return order.status === "Pending"; + if (filter === "In Transit") return order.status === "Shipped" || order.status === "Processing"; + return true; + }); + + const indexOfLastOrder = currentPage * ordersPerPage; + const indexOfFirstOrder = indexOfLastOrder - ordersPerPage; + const currentOrders = filteredOrders.slice(indexOfFirstOrder, indexOfLastOrder); + + const totalPages = Math.ceil(filteredOrders.length / ordersPerPage); + + const paginate = (pageNumber:any) => setCurrentPage(pageNumber); + + return ( +
+

My Orders

+
+ +
+
+ {currentOrders.map((order) => ( +
+
+
+

Id:

+

{order.id}

+
+
+

Status:

+

{order.status}

+
+
+
+

{order.date}

+
+

Total Price:

+

{order.totalPrice}

+
+ +
+
+ ))} +
+
+ {Array.from({ length: totalPages }, (_, index) => ( + + ))} +
+
+ ); +} + +export default Orders; diff --git a/src/containers/buyer/PendingPayments.tsx b/src/containers/buyer/PendingPayments.tsx new file mode 100644 index 0000000..a87446c --- /dev/null +++ b/src/containers/buyer/PendingPayments.tsx @@ -0,0 +1,98 @@ +import { useState } from "react"; +import { useDispatch } from "react-redux"; +import { setActiveMenu } from "../../redux/slices/buyerDashboard"; + +function PendingPayments() { + const dispatch = useDispatch(); + + // Dummy orders data + const orders = [ + { + id: "764376473647634736", + status: "Pending", + date: "07/14/2024", + totalPrice: "$566", + }, + { + id: "998877665544332211", + status: "Pending", + date: "07/15/2024", + totalPrice: "$780", + }, + { + id: "554433221199887766", + status: "Pending", + date: "07/16/2024", + totalPrice: "$350", + }, + { + id: "332211998877665544", + status: "Pending", + date: "07/17/2024", + totalPrice: "$450", + }, + { + id: "123456789012345678", + status: "Pending", + date: "07/18/2024", + totalPrice: "$1200", + }, + ]; + + const [currentPage, setCurrentPage] = useState(1); + const ordersPerPage = 3; + + const indexOfLastOrder = currentPage * ordersPerPage; + const indexOfFirstOrder = indexOfLastOrder - ordersPerPage; + const currentOrders = orders.slice(indexOfFirstOrder, indexOfLastOrder); + + const totalPages = Math.ceil(orders.length / ordersPerPage); + + const paginate = (pageNumber:any) => setCurrentPage(pageNumber); + + return ( +
+

Pending Payments

+
+ {currentOrders.map((order) => ( +
+
+
+

Id:

+

{order.id}

+
+
+

Status:

+

{order.status}

+
+
+
+

{order.date}

+
+

Total Price:

+

{order.totalPrice}

+
+ +
+
+ ))} +
+
+ {Array.from({ length: totalPages }, (_, index) => ( + + ))} +
+
+ ); +} + +export default PendingPayments; diff --git a/src/containers/buyer/Profile.tsx b/src/containers/buyer/Profile.tsx new file mode 100644 index 0000000..329869a --- /dev/null +++ b/src/containers/buyer/Profile.tsx @@ -0,0 +1,81 @@ +import { useState } from 'react'; + +function Profile() { + const [email, setEmail] = useState(''); + const [profilePhoto] = useState(null); + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + + const handleSubmit = () => { + console.log({ email, profilePhoto, firstName, lastName }); + }; + + return ( +
+

Update Profile

+
+
+ + setFirstName(e.target.value)} + required + /> +
+
+ + setLastName(e.target.value)} + required + /> +
+
+ + setEmail(e.target.value)} + required + /> +
+
+ + + {profilePhoto && Profile} +
+
+ +
+
+
+ ); +} + +export default Profile; diff --git a/src/containers/buyer/ResetPassword.tsx b/src/containers/buyer/ResetPassword.tsx new file mode 100644 index 0000000..5088701 --- /dev/null +++ b/src/containers/buyer/ResetPassword.tsx @@ -0,0 +1,61 @@ +function ResetPassword() { + return ( +
+

Reset Password

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ ); +} + +export default ResetPassword; diff --git a/src/containers/buyer/ReturnRefund.tsx b/src/containers/buyer/ReturnRefund.tsx new file mode 100644 index 0000000..dfad87c --- /dev/null +++ b/src/containers/buyer/ReturnRefund.tsx @@ -0,0 +1,7 @@ +function ReturnRefund() { + return ( +
Return and Refund
+ ) +} + +export default ReturnRefund \ No newline at end of file diff --git a/src/containers/buyer/Shipping.tsx b/src/containers/buyer/Shipping.tsx new file mode 100644 index 0000000..b965bb4 --- /dev/null +++ b/src/containers/buyer/Shipping.tsx @@ -0,0 +1,7 @@ +function Shipping() { + return ( +
Shipping
+ ) +} + +export default Shipping \ No newline at end of file diff --git a/src/pages/BuyerProfile.tsx b/src/pages/BuyerProfile.tsx new file mode 100644 index 0000000..867b7ad --- /dev/null +++ b/src/pages/BuyerProfile.tsx @@ -0,0 +1,93 @@ +import Footer from "../components/footer/Footer"; +import Navbar from "../components/navbar/Navbar"; +import Account from "../containers/buyer/Account"; +import Shipping from "../containers/buyer/Shipping"; +import ResetPassword from "../containers/buyer/ResetPassword"; +import Orders from "../containers/buyer/Orders"; +import { setActiveMenu } from "../redux/slices/buyerDashboard"; +import { useSelector } from "react-redux"; +import { useDispatch } from "react-redux"; +import PendingPayments from "../containers/buyer/PendingPayments"; +import FeedBack from "../containers/buyer/FeedBack"; +import InTransit from "../containers/buyer/InTransit"; +import ReturnRefund from "../containers/buyer/ReturnRefund"; +import Profile from "../containers/buyer/Profile"; +import OrderDetails from "../containers/buyer/OderDetails"; + + +function BuyerProfile() { +const activeMenu = useSelector((state:any) => state.activeMenu.activeMenu); +const dispatch = useDispatch(); + + const renderContent = () => { + switch (activeMenu) { + case "account": + return ; + case "orders": + return ; + case "shipping": + return ; + case "pendingpayments": + return ; + case "returnrefund": + return ; + case "feedback": + return ; + case "intransit": + return ; + case "reset": + return ; + case "profile": + return ; + case "order-details": + return ; + default: + return
Select a menu item
; + } + }; + //BuyerProfile + return ( +
+ +
+
+
+ Buyer Dashboard +
+
    +
  • dispatch(setActiveMenu("account"))} + > + My Account +
  • +
  • dispatch(setActiveMenu("orders"))} + > + My Orders +
  • +
  • dispatch(setActiveMenu("shipping"))} + > + Shipping Address +
  • +
  • dispatch(setActiveMenu("reset"))} + > + Reset Password +
  • +
+
+
+ {renderContent()} +
+
+
+ ); +} + +export default BuyerProfile; diff --git a/src/redux/slices/buyerDashboard.tsx b/src/redux/slices/buyerDashboard.tsx new file mode 100644 index 0000000..0f7dcc7 --- /dev/null +++ b/src/redux/slices/buyerDashboard.tsx @@ -0,0 +1,19 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const initialState = { + activeMenu: "account", +}; + +const activeMenuSlice = createSlice({ + name: "activeMenu", + initialState, + reducers: { + setActiveMenu: (state, action) => { + state.activeMenu = action.payload; + }, + }, +}); + +export const { setActiveMenu } = activeMenuSlice.actions; + +export default activeMenuSlice.reducer; diff --git a/src/redux/store.ts b/src/redux/store.ts index f650c7c..7d40334 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -6,6 +6,7 @@ import registerReducer from './slices/registerSlice'; import userReducer from './slices/userSlice'; import productReducer from './slices/productsSlice'; import categoriesReducer from './slices/categorySlice'; +import activeMenuReducer from './slices/buyerDashboard'; export const store = configureStore({ reducer: { @@ -13,6 +14,7 @@ export const store = configureStore({ user: userReducer, register: registerReducer, category: categoriesReducer, + activeMenu: activeMenuReducer, [mavericksApi.reducerPath]: mavericksApi.reducer, }, middleware: getDefaultMiddleware => getDefaultMiddleware().concat(mavericksApi.middleware),