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 15, 2024
1 parent f38f190 commit 24684eb
Show file tree
Hide file tree
Showing 15 changed files with 1,023 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -88,6 +89,10 @@ const App = () => {
path: 'products/:id',
element: <ProductDetail />,
},
{
path: 'buyer-profile',
element: <BuyerProfile />,
}
],
},
{
Expand Down
78 changes: 78 additions & 0 deletions src/containers/buyer/Account.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<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={image} alt="Buyer Profile" className="rounded-full mr-4 w-16 h-16 object-cover" />
<div>
<h2 className="text-xl font-semibold">NP Leon</h2>
<p className="text-gray-600">Buyer</p>
</div>
</div>
<FaUserEdit className="text-2xl text-gray-600 cursor-pointer" onClick={() => dispatch(setActiveMenu("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("pendingpayments"))}>
<div className="absolute top-2 right-2 bg-red-500 text-white text-sm rounded-full w-8 h-8 flex items-center justify-center">
5
</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>

{/* All 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("intransit"))}
>
<div className="absolute top-2 right-2 bg-red-500 text-white text-sm rounded-full w-8 h-8 flex items-center justify-center">
5
</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"
onClick={() => dispatch(setActiveMenu("feedback"))}
>
<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"
onClick={() => dispatch(setActiveMenu("returnrefund"))}
>
<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;
153 changes: 153 additions & 0 deletions src/containers/buyer/FeedBack.tsx
Original file line number Diff line number Diff line change
@@ -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<Product[]>(productsData);
const [modalOpen, setModalOpen] = useState(false);
const [currentProduct, setCurrentProduct] = useState<Product | null>(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 (
<div className="max-w-4xl mx-auto bg-gray-50 p-8 rounded-lg shadow-lg">
<h1 className="text-3xl font-bold mb-8 text-center text-gray-800">Product List</h1>
{currentProducts.map((product) => (
<div key={product.id} className="mb-8 p-6 bg-white rounded-lg shadow-md border border-gray-200">
<div className="flex justify-between items-center mb-4">
<div>
<h2 className="text-2xl font-semibold text-gray-800">{product.name}</h2>
<p className="text-gray-600">Average Rating: {calculateAverageRating(product.ratings)}</p>
</div>
<div className="flex items-center">
{product.ratings.length === 0 ? (
<button
onClick={() => handleAddFeedback(product)}
className="bg-blue-600 text-white px-4 py-2 rounded shadow hover:bg-blue-700 transition duration-200"
>
Add Feedback
</button>
) : (
<div className="flex flex-col space-y-2">
{product.ratings.map((rating, index) => (
<div key={index} className="flex justify-between items-center">
<p className="text-gray-600">
Feedback {index + 1}: {rating} stars
</p>
<button
onClick={() => handleDeleteFeedback(product.id, index)}
className="text-red-500 hover:text-red-700 transition duration-200"
>
Delete
</button>
</div>
))}
</div>
)}
</div>
</div>
</div>
))}
<div className="mt-6 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-greenColor text-white'
: 'bg-whiteColor 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}
/>
)}
</div>
);
};

export default FeedBack;
98 changes: 98 additions & 0 deletions src/containers/buyer/FeedbackModal.tsx
Original file line number Diff line number Diff line change
@@ -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<FeedbackModalProps> = ({ productName, isOpen, onClose, onSubmit }) => {
const [message, setMessage] = useState('');
const [image, setImage] = useState<File | null>(null);
const [rating, setRating] = useState(0);

const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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 (
<div className="fixed inset-0 flex items-center justify-center z-50 bg-blackColor bg-opacity-50">
<div className="bg-whiteColor p-6 rounded-lg shadow-md w-full max-w-md">
<h2 className="text-xl font-bold mb-4">Add Feedback for {productName}</h2>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="message">
Feedback Message
</label>
<textarea
id="message"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring focus:border-blue-300"
value={message}
onChange={(e) => setMessage(e.target.value)}
required
></textarea>
</div>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="image">
Upload Image (optional)
</label>
<input
type="file"
id="image"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring focus:border-blue-300"
onChange={handleImageChange}
accept="image/*"
/>
</div>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="rating">
Rating
</label>
<select
id="rating"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring focus:border-blue-300"
value={rating}
onChange={(e) => setRating(Number(e.target.value))}
required
>
<option value={0} disabled>Select rating</option>
{[1, 2, 3, 4, 5].map((rate) => (
<option key={rate} value={rate}>
{rate} star{rate > 1 ? 's' : ''}
</option>
))}
</select>
</div>
<div className="flex justify-end">
<button
type="button"
className="bg-gray-500 text-white px-4 py-2 rounded mr-2 hover:bg-gray-700"
onClick={onClose}
>
Cancel
</button>
<button
type="submit"
className="bg-darkGreen text-whiteColor px-4 py-2 rounded hover:bg-greenColor"
>
Submit
</button>
</div>
</form>
</div>
</div>
);
};

export default FeedbackModal;
Loading

0 comments on commit 24684eb

Please sign in to comment.