Skip to content

Commit

Permalink
feat(wishes):buyer should be able wish a products
Browse files Browse the repository at this point in the history
-Buyer should wish a products
-Buyer should add a wished products to cart
-Seller should be able to see a wished products

[Deliver #187419141]
  • Loading branch information
niyobertin committed Jul 21, 2024
1 parent a738f89 commit b44cf44
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 40 deletions.
9 changes: 7 additions & 2 deletions src/components/cards/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ const ProductCard: React.FC<IProductCardProps> = ({ product }) => {
const { wishes } = useSelector((state: RootState) => state.wishes);
const dispatch = useAppDispatch();
const navigate = useNavigate();

const loggedInUserToken = localStorage.getItem("accessToken");
let loggedInUser;
if (loggedInUserToken) {
// @ts-ignore
loggedInUser = JSON.parse(atob(loggedInUserToken.split(".")[1]));
}
const formatPrice = (price: number) => {
if (price < 1000) {
return price.toString();
Expand Down Expand Up @@ -216,7 +221,7 @@ const ProductCard: React.FC<IProductCardProps> = ({ product }) => {
className="bg-white"
sx={{ paddingY: 0.5, paddingX: 0.5 }}
>
{!localStorage.getItem("accessToken") ? (
{!loggedInUserToken || loggedInUser.roleId !== 1 ? (
""
) : loadWishes ? (
<Spinner color="pink" aria-label="Pink spinner example" />
Expand Down
13 changes: 9 additions & 4 deletions src/components/common/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@ interface ISerachProps {

const Header: React.FC<ISerachProps> = ({ searchQuery, setSearchQuery }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const { profile } = useSelector((state: RootState) => state.usersProfile);
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(e.target.value);
};

let userInfo;
const accessToken = localStorage.getItem("accessToken");
if (accessToken) {
userInfo = JSON.parse(atob(accessToken.split(".")[1]));
}
const dispatch = useAppDispatch();

useEffect(() => {
// @ts-ignore
dispatch(getProfile());
}, [dispatch]);

useEffect(() => {
if (accessToken) {
try {
Expand Down Expand Up @@ -106,7 +111,7 @@ const Header: React.FC<ISerachProps> = ({ searchQuery, setSearchQuery }) => {
""
) : (
<div className="absolute w-5 h-5 bg-red-500 -top-3 -right-3 rounded-full text-center text-white text-[12px] flex justify-center items-center">
<span className="">{userCart.length}</span>
<span className="">{userCart && userCart.length}</span>
</div>
)}
</Stack>
Expand All @@ -117,10 +122,10 @@ const Header: React.FC<ISerachProps> = ({ searchQuery, setSearchQuery }) => {
<CiUser className="text-[24px] text-black" />
<Stack className="flex flex-col">
<span className="ml-2 font-semibold text-[12px]">
{userInfo.name}
{profile && profile.fullName}
</span>
<span className="ml-2 font-semibold text-[12px]">
{userInfo.email}
{profile && profile?.email}
</span>
</Stack>
</Link>
Expand Down
7 changes: 4 additions & 3 deletions src/components/dashboard/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ interface SidebarProps {
const SideBar: React.FC<SidebarProps> = ({ isOpen }) => {
const location = useLocation();

const getLinkClass = (path: string) => (location.pathname === path ? "text-primary" : "text-dark-gray");
const getLinkClass = (path: string) =>
(location.pathname === path ? "text-primary" : "text-dark-gray");

return (
<div
Expand Down Expand Up @@ -71,9 +72,9 @@ const SideBar: React.FC<SidebarProps> = ({ isOpen }) => {
</p>
</NavLink>
<NavLink
to="/wishes"
to="/dashboard/wishes"
className={`py-2.5 px-4 flex items-center gap-2 text-lg rounded transition duration-200 ${getLinkClass(
"/wishes",
"/dashboard/wishes",
)}`}
>
<MdInsertChartOutlined className="text-xl" />
Expand Down
100 changes: 100 additions & 0 deletions src/components/dashboard/wishesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

import { fetchWishes } from "../../redux/reducers/wishListSlice";
import { RootState, AppDispatch } from "../../redux/store";

import Spinner from "./Spinner";

const WishesTable: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const { wishes, isLoading, error } = useSelector(
(state: RootState) => state.wishes,
);
useEffect(() => {
const fetchData = async () => {
try {
await dispatch(fetchWishes());
} catch (error) {
console.error(error);
}
};

fetchData();
}, [dispatch]);

return (
<div className="relative">
<div className="overflow-x-scroll ">
<table className="min-w-full bg-white border-collapse">
<thead className="border-b-2">
<tr className="px-3 bg-light-blue rounded-lg">
<th className="pr-6 rounded-l-[12px] py-2 pl-4 text-sm font-medium text-left whitespace-nowrap text-black">
Product
</th>
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap">
Stock Quantity
</th>
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap">
Price
</th>
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap">
Buyer
</th>
<th className="py-2 px-4 text-sm font-medium text-left text-black whitespace-nowrap">
Email
</th>
</tr>
</thead>
<tbody>
{isLoading ? (
<tr>
<td colSpan={9} className="text-center py-4">
<Spinner />
</td>
</tr>
) : wishes.length === 0 ? (
<tr>
<td
colSpan={6}
className="text-center py-10 text-dark-gray dark:text-white"
>
No products found.
</td>
</tr>
) : (
wishes.map((item) => (
<tr key={item.id} className="px-2">
<td className="pl-4 pr-10 xl:pr-10 py-2 flex items-center whitespace-nowrap space-x-3">
<img
src={item.product.images[0]}
alt={item.product.name}
className="w-10 h-10 object-cover mr-2"
/>
<span className="text-dark-gray text-sm">
{item?.product.name}
</span>
</td>
<td className="py-3 px-4 text-gray-700 text-sm">
{item.product.stockQuantity}
</td>
<td className="py-3 px-4 text-gray-700 text-sm">
{item.product.price}
</td>
<td className="py-3 px-4 text-gray-700 text-sm whitespace-nowrap">
{item.user.name}
</td>
<td className="py-3 px-4 text-sm text-gray-700">
{item.user.email}
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
);
};

export default WishesTable;
18 changes: 18 additions & 0 deletions src/dashboard/sellers/wishesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Layout from "../../components/layouts/SellerLayout";
import WishesTable from "../../components/dashboard/wishesTable";

const Wishes = () => (
<Layout>
<div className="mt-24 mb-4">
<h1 className="text-2xl font-medium text-black">Wishes List</h1>
<p className="text-dark-gray">
Detailed information about your wished products
</p>
</div>
<section className="lg:pl-5 bg-white relative xl:w-full px-4">
<WishesTable />
</section>
</Layout>
);

export default Wishes;
2 changes: 1 addition & 1 deletion src/pages/ReviewList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const ReviewsList: React.FC<ReviewsListProps> = ({ productId }) => {
setNewRating("");
setNewFeedback("");
// @ts-ignore
if (response.error) {
if (response && response.error) {
// @ts-ignore
toast.error(response.payload.message);
} else {
Expand Down
36 changes: 22 additions & 14 deletions src/pages/Wishes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@ import { toast, ToastContainer } from "react-toastify";
import { MdOutlineClose } from "react-icons/md";
import { AxiosError } from "axios";
import { Spinner } from "flowbite-react";
import { useNavigate } from "react-router-dom";

import MainSpinner from "../components/common/auth/Loader";
import Warning from "../components/common/notify/Warning";
import { deleteWish, fetchWishes } from "../redux/reducers/wishListSlice";
import { RootState, AppDispatch } from "../redux/store";
import { addToCart } from "../redux/reducers/cartSlice";
import LinkToUpdatePage from "../components/profile/linkToUpdate";

// @ts-ignore
const BuyerWishesList: React.FC = () => {
const dispatch: AppDispatch = useDispatch();
const { wishes, error } = useSelector((state: RootState) => state.wishes);
const [loadingWish, setLoadingWish] = useState<number | null>(null);
const [isLoading, setLoading] = useState<boolean>(false);
const navigate = useNavigate();
const loggedInUserToken = localStorage.getItem("accessToken");
let loggedInUser;
if (loggedInUserToken) {
// @ts-ignore
loggedInUser = JSON.parse(atob(loggedInUserToken.split(".")[1]));
}
useEffect(() => {
setLoading(true);
const fetchData = async () => {
Expand All @@ -31,13 +40,12 @@ const BuyerWishesList: React.FC = () => {

fetchData();
}, [dispatch]);

const handleDeleteWish = async (productId) => {
setLoadingWish(productId);
try {
await dispatch(deleteWish({ productId }));
const response = await dispatch(deleteWish({ productId }));
dispatch(fetchWishes());
toast.success("Product removed from your wish list");
toast.success(response.payload);
} catch (err) {
const error = err as AxiosError;
toast.error(error.message);
Expand Down Expand Up @@ -71,23 +79,23 @@ const BuyerWishesList: React.FC = () => {
</p>
);
}
if (!localStorage.getItem("accessToken")) {
return <Warning />;
if (!loggedInUserToken) {
navigate("/login");
}

if (error) {
return (
<div className="w-full h-[60vh] flex justify-center items-center">
<h2>{error}</h2>
</div>
);
if (loggedInUserToken && loggedInUser.roleId !== 1) {
navigate("/");
}

return (
<div className="w-full px-[2%] md:px-[4%]">
<ToastContainer />
<div className="pt-8">
<h2>Home / Wishes</h2>
<h2>
<LinkToUpdatePage link="/">
<span className="hover:text-[#DB4444]">Home </span>
</LinkToUpdatePage>
/ Wishes
</h2>
</div>
<div className="overflow-x-auto">
<table className="min-w-full my-4 overflow-x-auto">
Expand All @@ -107,7 +115,7 @@ const BuyerWishesList: React.FC = () => {
{wishes.length === 0 ? (
<tr>
<td colSpan={4} className="text-center">
No wishes found
No wishes found 😎
</td>
</tr>
) : (
Expand Down
17 changes: 10 additions & 7 deletions src/pages/updateProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useSelector } from "react-redux";
import { ToastContainer, toast } from "react-toastify";
import { AxiosError } from "axios";
import { FaCircleUser } from "react-icons/fa6";
import { useNavigate } from "react-router-dom";

import LinkPages from "../components/common/auth/LinkPages";
import { RootState } from "../redux/store";
Expand All @@ -30,6 +31,7 @@ export const convertUrlToFile = async (url: string): Promise<File> => {

const UpdateUserProfile: React.FC = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const { profile } = useSelector((state: RootState) => state.usersProfile);
useEffect(() => {
// @ts-ignore
Expand Down Expand Up @@ -77,7 +79,6 @@ const UpdateUserProfile: React.FC = () => {
setImagePreview(reader.result as string);
};
reader.readAsDataURL(file);
// @ts-ignore
setSelectedImage(file);
}
};
Expand All @@ -99,8 +100,7 @@ const UpdateUserProfile: React.FC = () => {
const formData = new FormData();
if (selectedImage) {
formData.append("profileImage", selectedImage);
}
if (profile?.profileImage) {
} else if (profile?.profileImage) {
// @ts-ignore
const newFile = await convertUrlToFile(profile.profileImage);
formData.append("profileImage", newFile);
Expand Down Expand Up @@ -137,7 +137,10 @@ const UpdateUserProfile: React.FC = () => {
{ value: "RWF", label: "RWF" },
{ value: "USD", label: "USD" },
];

const loggedInUserToken = localStorage.getItem("accessToken");
if (!loggedInUserToken) {
navigate("/login");
}
return (
<div className="parent-container h-screen overflow-auto">
<div className="sticky top-0 w-[92%] ml-[4%] mr-[4%]">
Expand All @@ -147,7 +150,7 @@ const UpdateUserProfile: React.FC = () => {
<div className="w-[92%] overflow-y-hidden ml-[4%] mr-[4%]bg-yellow-100">
<LinkToUpdatePage link="/">
<p className="border-solid sm:border-none border-b-[1px] border-gray-200 ">
<span className="text-gray-400">Home</span>
<span className="text-gray-400 hover:text-[#DB4444]">Home</span>
/My Account
</p>
</LinkToUpdatePage>
Expand Down Expand Up @@ -206,11 +209,11 @@ const UpdateUserProfile: React.FC = () => {
</span>
)}
</label>
<BiImageAdd className="mt-[-30px] inset-0 bg-[#DB4444] h-[30%] w-[10%] text-white rounded-[50%]" />
<BiImageAdd className="mt-[-25px] ml-14 inset-0 bg-[#DB4444] h-[25%] w-[6%] text-white rounded-[50%]" />
</div>

<ProfileTextInput
label="Full name"
label="Name"
defaultValue={profile?.fullName}
register={register}
name="fullName"
Expand Down
Loading

0 comments on commit b44cf44

Please sign in to comment.