Skip to content

Commit

Permalink
[finishes #187943570] buyer dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
amin-leon committed Jul 22, 2024
1 parent a6ea4c6 commit 8869151
Show file tree
Hide file tree
Showing 37 changed files with 1,795 additions and 15 deletions.
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import Cart from './components/cart/Cart';
import { cartApi } from './services/cartApi';
import VerifyOTPPage from './pages/VerifyOTPPage';

import BuyerRestrictedRoutes from './containers/buyer/BuyerRestrictedRoutes';
const App = () => {
const { data, error, isLoading } = useGetProductsQuery();
const dispatch = useDispatch();
Expand Down Expand Up @@ -114,6 +115,10 @@ const App = () => {
path: "/checkoutbag",
element: <Checkout />
},
{
path: 'buyer-profile',
element: <BuyerRestrictedRoutes role='buyer' />,
}
],
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import { useGetUserByIdQuery } from '../../services/userApi';
import { setProfile } from '../../redux/slices/userSlice';
const Chat: React.FC = () => {
const [showChat, setShowChat] = useState(false);
const userToken = useAppSelector(state => state.user.token);
const userToken:any = useAppSelector(state => state.user.token);
const dispatch = useAppDispatch();
const navigate = useNavigate();
const scrollDown = useRef<HTMLDivElement>(null);
// set profile image
const profileImage: string | null = useAppSelector(state => state.user.photoUrl);
const profileImage: File | null = useAppSelector(state => state.user.photoUrl);
const userId = useAppSelector(state => state.user.userId);
const { data, isError, isSuccess, isLoading } = useGetMessagesQuery(userToken as string, { skip: !userToken });
const { data: userData } = useGetUserByIdQuery(userId, { skip: !userId });
Expand Down
2 changes: 1 addition & 1 deletion src/components/navbar/wishNav/WishNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const WishNav: React.FC<WishNav> = ({ isLoading, userInfo }) => {
<p className='text-xl font-medium '>{firstName + ' ' + lastName}</p>
<div className='flex items-center gap-3 mt-5 mb-2'>
<LuUser className='size-6 md:size-8' strokeWidth={1} stroke='currentColor' />{' '}
<Link to='/' className='hover:underline font-light text-base'>
<Link to='/buyer-profile' className='hover:underline font-light text-base'>
Account Settings
</Link>
</div>
Expand Down
87 changes: 87 additions & 0 deletions src/containers/buyer/Account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// components/menu/Account.tsx
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";

interface AccountProps {
lastName: string;
photoUrl: string;
firstName: string;
email?: string;
orderss: any[];
data: any[];
Role: any
}

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: React.FC<AccountProps> = ({ firstName, Role, lastName, photoUrl, orderss }) => {
const dispatch = useDispatch();
const {data}:any = orderss

return (
<div className="p-6">
{/* User Profile Section */}
<div className="flex justify-between items-center bg-white shadow-md p-4 rounded-lg mb-6">
<div className="flex items-center">
<img src={photoUrl ? photoUrl : image} alt="profile" className="rounded-full mr-4 w-16 h-16 object-cover" />
<div>
<h2 className="text-xl font-semibold">{lastName} {firstName}</h2>
<p className="text-gray-600">{Role? Role.name: 'buyer'} </p>
</div>
</div>
<FaUserEdit className="text-2xl text-gray-600 cursor-pointer" onClick={() => dispatch(setActiveMenu({ activeMenu: "profile" }))} />
</div>

{/* Cards Section */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 py-10">
{/* Pending Card */}
<div className="bg-whiteColor cursor-pointer shadow-md p-8 rounded-lg flex flex-col items-center transform transition duration-300 hover:scale-105 relative"
onClick={() => dispatch(setActiveMenu({ activeMenu: "pendingpayments" }))}>
<div className="absolute top-2 right-2 bg-redColor text-whiteColor text-sm rounded-full w-8 h-8 flex items-center justify-center">
{data.filter((order: { status: string; }) => order.status === 'pending').length}
</div>
<FaHourglassHalf className="text-6xl text-greenColor mb-4 w-full text-center" />
<div className="text-center">
<h3 className="text-lg font-semibold">Pending Payments</h3>
</div>
</div>

{/* In Transit Card */}
<div className="bg-whiteColor cursor-pointer shadow-md p-8 rounded-lg flex flex-col items-center transform transition duration-300 hover:scale-105 relative"
onClick={() => dispatch(setActiveMenu({ activeMenu: "intransit" }))}>
<div className="absolute top-2 right-2 bg-redColor text-whiteColor text-sm rounded-full w-8 h-8 flex items-center justify-center">
{data.filter((order: { status: string; }) => order.status === 'in-transit').length}
</div>
<FaTruckFast className="text-6xl text-greenColor mb-4 w-full text-center" />
<div className="text-center">
<h3 className="text-lg font-semibold">In Transit</h3>
</div>
</div>

{/* Feedback Card */}
<div className="bg-whiteColor cursor-pointer shadow-md p-8 rounded-lg flex flex-col items-center transform transition duration-300 hover:scale-105 relative"
>
<MdOutlineFeedback className="text-6xl text-greenColor mb-4 w-full text-center" />
<div className="text-center">
<h3 className="text-lg font-semibold">Feedback</h3>
</div>
</div>

{/* Refund & Return Card */}
<div className="bg-whiteColor cursor-pointer shadow-md p-8 rounded-lg flex flex-col items-center transform transition duration-300 hover:scale-105 relative"
>
<PiKeyReturn className="text-6xl text-greenColor mb-4 w-full text-center" />
<div className="text-center">
<h3 className="text-lg font-semibold">Return & Refund</h3>
</div>
</div>
</div>
</div>
);
};

export default Account;
34 changes: 34 additions & 0 deletions src/containers/buyer/BuyerRestrictedRoutes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { RootState } from '../../redux/store';
import AdminLayout from '../../components/admin/AdminLayout';
import BuyerProfile from '../../pages/BuyerProfile';

interface RestrictedRouteProp {
role: 'admin' | 'buyer' | 'seller';
}

const BuyerRestrictedRoutes: React.FC<RestrictedRouteProp> = ({ role }) => {
const userRole = useSelector((state: RootState) => state.user.role);
const token = useSelector((state: RootState) => state.user.token);

// orders
// user profile


if (!token || userRole !== role) {
return <Navigate to='/' />;
}

switch (role) {
case 'admin':
return <AdminLayout />;
case 'buyer':
return <BuyerProfile />;
default:
return <Navigate to='/' />;
}
};

export default BuyerRestrictedRoutes;
37 changes: 37 additions & 0 deletions src/containers/buyer/ConfirmationModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';

interface ConfirmationModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
message: string;
}

const ConfirmationModal: React.FC<ConfirmationModalProps> = ({ isOpen, onClose, onConfirm, message }) => {
if (!isOpen) return null;

return (
<div className="fixed inset-0 flex items-center justify-center bg-blackColor bg-opacity-50">
<div className="bg-whiteColor p-6 rounded-lg shadow-lg max-w-sm mx-auto">
<h2 className="text-lg font-semibold mb-4">Confirm Action</h2>
<p className="mb-4">{message}</p>
<div className="flex justify-end gap-4">
<button
className="px-4 py-2 bg-gray-300 text-gray-800 rounded-md hover:bg-gray-400"
onClick={onClose}
>
Cancel
</button>
<button
className="px-4 py-2 bg-redColor text-whiteColor rounded-md cursor-pointer"
onClick={onConfirm}
>
Confirm
</button>
</div>
</div>
</div>
);
};

export default ConfirmationModal;
11 changes: 11 additions & 0 deletions src/containers/buyer/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// ErrorPage.js

const ErrorPage = () => {
return (
<div className="text-center text-lg font-semibold text-red-600">
'An unexpected error occurred
</div>
);
};

export default ErrorPage;
159 changes: 159 additions & 0 deletions src/containers/buyer/FeedBack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React, { useState } from 'react';
import FeedbackModal from './FeedbackModal';
import { FaStar, FaRegStar } from 'react-icons/fa';

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],
},
{
id: '3',
name: 'Product 3',
price: '$300',
manufacturer: 'Manufacturer C',
ratings: [3],
},
];

const FeedBack: React.FC = () => {
const [products, setProducts] = useState<Product[]>(productsData);
const [modalOpen, setModalOpen] = useState(false);
const [currentProduct, setCurrentProduct] = useState<Product | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const productsPerPage = 3;

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 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
)
);
};

const renderStars = (rating: number) => {
const stars = [];
for (let i = 1; i <= 5; i++) {
stars.push(i <= rating ? <FaStar key={i} /> : <FaRegStar key={i} />);
}
return stars;
};

return (
<div className="max-w-4xl p-8 rounded-md shadow-lg">
<h1 className="text-3xl font-bold mb-8 text-gray-800">Purchased Products</h1>
{currentProducts.map((product, index) => (
<div
key={product.id}
className={`mb-4 p-2 rounded-md shadow-sm ${index % 2 === 0 ? 'bg-grayColor' : 'bg-whiteColor'}`}
>
<div className="flex justify-between items-center mb-2">
<div>
<h2 className="text-xl font-semibold text-gray-800">{product.name}</h2>
</div>
<div className="flex items-center">
{product.ratings.length === 0 ? (
<button
onClick={() => handleAddFeedback(product)}
className="bg-darkGreen text-whiteColor px-3 py-1 rounded shadow hover:bg-greenColor transition duration-200"
>
Add Feedback
</button>
) : (
<div className="flex flex-col space-y-1">
{product.ratings.map((rating, index) => (
<div key={index} className="flex justify-between items-center gap-4">
<div className="flex items-center">
<div className="flex">{renderStars(rating)}</div>
</div>
<button
onClick={() => handleDeleteFeedback(product.id, index)}
className="bg-redColor text-whiteColor px-2 py-1 rounded shadow transition duration-200"
>
Delete
</button>
</div>
))}
</div>
)}
</div>
</div>
</div>
))}
<div className="mt-4 flex justify-end">
{Array.from({ length: totalPages }, (_, index) => (
<button
key={index}
onClick={() => paginate(index + 1)}
className={`mx-1 px-3 py-1 border rounded ${
currentPage === index + 1
? 'bg-green-500 text-white'
: 'bg-white border-gray-300 text-gray-800 hover:bg-gray-100'
}`}
>
{index + 1}
</button>
))}
</div>
{currentProduct && (
<FeedbackModal
productName={currentProduct.name}
isOpen={modalOpen}
onClose={handleModalClose}
onSubmit={handleFeedbackSubmit}
productId={currentProduct.id}
/>
)}
</div>
);
};

export default FeedBack;
Loading

0 comments on commit 8869151

Please sign in to comment.