Skip to content

Commit

Permalink
Merge pull request #24 from atlp-rwanda/187802122-bug-fix-landing-pag…
Browse files Browse the repository at this point in the history
…e-fixing

[finishes #187802122] bgfix-landing-page
  • Loading branch information
niyontwali authored Jul 8, 2024
2 parents 6150a95 + fbca181 commit ea71b18
Show file tree
Hide file tree
Showing 19 changed files with 435 additions and 103 deletions.
Empty file added .env.example
Empty file.
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.11.0
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Buyers from './pages/admin/Buyers';
import Messages from './pages/admin/Messages';
import Settings from './pages/admin/Settings';

import CategoriesPage from './pages/CategoriesPage';
const App = () => {
const { data, error, isLoading } = useGetProductsQuery();
const dispatch = useDispatch();
Expand Down Expand Up @@ -65,6 +66,10 @@ const App = () => {
path: 'auth/success/:token',
element: <GoogleAuthSuccess />,
},
{
path: 'categories/:categoryId',
element: <CategoriesPage />,
}
],
},
{
Expand Down
Binary file added src/assets/Hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 18 additions & 9 deletions src/components/Products/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { FaHeart } from 'react-icons/fa6';
import { Product } from '../../types/Types';
import { TiShoppingCart } from 'react-icons/ti';
import StarRating from '../common/Ratings';

interface ProductCardProps {
product: Product;
}

const ProductCard: React.FC<ProductCardProps> = ({ product }) => {
const imageUrl = product?.images?.[0] ?? 'default-image-url';
const price = product?.sizes?.[0]?.price ?? 'default-image-url';
const imageUrl = product?.images?.[0] ?? '';
const price = product?.sizes?.[0]?.price ?? '';

return (
<div className="product-card">
<div className="product-image">
<div className="product-card bg-whiteColor shadow-lg rounded-lg p-2 m-4 md:p-4 md:m-4 transition-transform hover:scale-105 cursor-pointer">
<div className="product-image flex justify-center">
<img
src={imageUrl}
alt={product.name}
className="h-48 w-full rounded-sm object-cover"
className="w-32 h-32 rounded-sm object-cover md:h-48 md:w-48"
/>
</div>
<div className="product-name-cart-button pt-2">
Expand All @@ -25,10 +28,16 @@ const ProductCard: React.FC<ProductCardProps> = ({ product }) => {
</div>
<p className="text-sm p-2 text-[#949191]">{product.manufacturer}</p>
</div>
<div className="cart-button">
<button className="px-5 py-2 text-center bg-[#007A7A] text-whiteColor font-bold rounded-sm">
Add to Cart
</button>
<div className="cart-button mt-4">
<div className="cart-wish-icons-ratings flex justify-between">
<div className="cart-wish-icons text-2xl flex gap-2">
<TiShoppingCart className='cursor-pointer'/>
<FaHeart className='cursor-pointer'/>
</div>
<div className="ratings flex ">
<StarRating reviews={product.reviews} />
</div>
</div>
</div>
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions src/components/categories/CategoriesSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const CategorySkeleton = () => {
const skeletons = Array.from({ length: 7 });

return (
<div className="scroll-container flex flex-row gap-4 overflow-x-auto snap-x h-48">
{skeletons.map((_, index) => (
<div key={index} className="single-category flex justify-center gap-1 flex-col cursor-pointer relative w-56 p-4">
<div className="image-skeleton bg-gray-300 h-48 rounded-md animate-pulse shadow-xl"></div>
<p className="text-skeleton bg-gray-300 h-6 w-3/4 mt-2 rounded-md animate-pulse text-grayColor">Loading</p>
<div className="absolute inset-0 hover:bg-blackColor hover:bg-opacity-20 rounded-md"></div>
</div>
))}
</div>
);
};

export default CategorySkeleton;
37 changes: 37 additions & 0 deletions src/components/common/Ratings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { FaStar, FaStarHalfAlt } from 'react-icons/fa';
import { CiStar } from 'react-icons/ci';
import { Review } from '../../types/Types';

interface RatingsProps {
reviews: Review[];
}

const StarRating: React.FC<RatingsProps> = ({ reviews }) => {
// Calculate the average rating
const calculateAverageRating = (reviews: Review[]): number => {
if (reviews.length === 0) return 0;
const totalRating = reviews.reduce((sum, review) => sum + review.rating, 0);
return totalRating / reviews.length;
};

const averageRating = calculateAverageRating(reviews);

const renderStars = () => {
const stars = [];
for (let i = 1; i <= 5; i++) {
if (averageRating >= i) {
stars.push(<FaStar key={i} />);
} else if (averageRating >= i - 0.5) {
stars.push(<FaStarHalfAlt key={i} />);
} else {
stars.push(<CiStar key={i} />);
}
}
return stars;
};

return <div className="ratings flex">{renderStars()}</div>;
};

export default StarRating;
1 change: 1 addition & 0 deletions src/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ const Navbar: React.FC = () => {
type='text'
className='w-full p-1 px-3 rounded-full text-blackColor outline-none text-xs py-2 md:text-sm md:py-2 font-light md:border md:border-blackColor'
placeholder='Search everything at Mavericks online'
required
onChange={e => setSearchQuery(e.target.value)}
/>
<button
Expand Down
11 changes: 9 additions & 2 deletions src/components/search/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@ interface SearchQueryProps {
const SearchResults = ({ searchQuery }: SearchQueryProps) => {
const { isLoading, productsDataList: productsList } = useSelector((state: any) => state.products);
const filteredProducts = [
...productsList.filter((product: Product) => product.name.toLowerCase().includes(searchQuery.toLowerCase())),
...productsList.filter((product: Product) => product.description.toLowerCase().includes(searchQuery.toLowerCase())),
...new Set([
...productsList.filter((product: Product) =>
product.name.toLowerCase().includes(searchQuery.toLowerCase())
),
...productsList.filter((product: Product) =>
product.description.toLowerCase().includes(searchQuery.toLowerCase())
)
])
];

useEffect(() => {
if (searchQuery.length !== 0 && filteredProducts.length !== 0) {
const productNames = filteredProducts.map(product => product.name);
Expand Down
7 changes: 4 additions & 3 deletions src/containers/Arrivals/NewArrivals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Product } from '../../types/Types';
import { BiSolidCircle } from 'react-icons/bi';
import { useSelector } from 'react-redux';

const perPage = 6;
const perPage = 8;

export default function NewArrivals() {
const [currentPage, setCurrentPage] = useState(0);
Expand All @@ -20,14 +20,15 @@ export default function NewArrivals() {

const startIndex = currentPage * perPage;
const endIndex = startIndex + perPage;
const allProductsOnPage = productsList.slice(startIndex, endIndex);
const sortedProducts = [...productsList].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()).slice(0, 13);
const allProductsOnPage = sortedProducts.slice(startIndex, endIndex);

return (
<div className='new-arrivals'>
<div className='arrivals-header py-5'>
<h1 className='text-3xl font-bold'>New Arrivals</h1>
</div>
<div className='grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6 gap-5'>
<div className='grid grid-cols-2 gap-0 sm:grid-cols-0 sm:gap-2 md:grid-cols-3 md:gap-3 lg:grid-cols-4 lg:gap-4'>
{isLoading
? Array.from({ length: perPage }).map((_, index) => <ProductCardSkeleton key={index} />)
: allProductsOnPage.map((product: Product) => <ProductCard key={product.id} product={product} />)}
Expand Down
2 changes: 1 addition & 1 deletion src/containers/FeaturedProducts/FeaturedProducts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function FeaturedProduct() {
<div className='featured-header py-5'>
<h1 className='text-3xl font-bold'>Featured Products</h1>
</div>
<div className='grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6 gap-5'>
<div className='grid grid-cols-2 gap-0 sm:grid-cols-0 sm:gap-2 md:grid-cols-3 md:gap-3 lg:grid-cols-4 lg:gap-4'>
{productsList.map((product: Product) => (
<ProductCard key={product.id} product={product} />
))}
Expand Down
18 changes: 12 additions & 6 deletions src/containers/categories/Category.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import React from "react"
import { useNavigate } from "react-router-dom";

interface CategoryProps {
image: string,
category: string
name: string
}

const Category:React.FC<CategoryProps> =({image, category}) =>{
const Category:React.FC<CategoryProps> =({image, name}) =>{
const navigate = useNavigate();

const handleClick = () => {
navigate(`/categories/${name}`);
};
return (
<div className="single-category flex justify-center gap-1 flex-col cursor-pointer relative w-56 p-4">
<img src={image} alt="Category" className=' h-48 rounded-md object-cover hover:w-56 hover:h-56'/>
<p className="text-blackColor font-bold">{category}</p>
<div className="absolute inset-0 hover:bg-blackColor hover:bg-opacity-50 rounded-md"></div>
<div className="single-category flex justify-center gap-1 flex-col cursor-pointer relative w-56 p-4" onClick={handleClick}>
<img src={image? image: "https://cdn-icons-png.freepik.com/512/4194/4194687.png"} alt="Category" className=' h-48 rounded-md object-cover hover:w-56 hover:h-56'/>
<p className="text-blackColor font-bold flex justify-center">{name}</p>
<div className="absolute inset-0 hover:bg-blackColor hover:bg-opacity-20 rounded-md"></div>
</div>
)
}
Expand Down
95 changes: 33 additions & 62 deletions src/containers/herosection/HeroPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,28 @@ import { IoShieldCheckmarkOutline } from "react-icons/io5";
import Category from '../categories/Category';
import NewArrivals from '../Arrivals/NewArrivals';
import FeaturedProduct from '../FeaturedProducts/FeaturedProducts';
import { useGetAllCategoriesQuery } from '../../services/productApi';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../../redux/store';
import { setIsCategoriesFetched, setallCategories } from '../../redux/slices/categorySlice';
import CategorySkeleton from '../../components/categories/CategoriesSkeleton';


const categories = [
{
image: "https://images.unsplash.com/photo-1498049794561-7780e7231661?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Electronics"
},
{
image: "https://images.unsplash.com/photo-1539109136881-3be0616acf4b?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Fashion"
},
{
image: "https://plus.unsplash.com/premium_photo-1669652639337-c513cc42ead6?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Books"
},
{
image: "https://images.unsplash.com/photo-1523575708161-ad0fc2a9b951?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Home & Garden"
},
{
image: "https://images.unsplash.com/photo-1576438162986-c685b1cfed7a?q=80&w=1576&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Sports"
},
{
image: "https://plus.unsplash.com/premium_photo-1684795780266-ecd819f04f96?q=80&w=1632&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Toys"
},
{
image: "https://images.unsplash.com/photo-1556982962-dc0ee0f77f47?q=80&w=1587&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Automotive"
},
{
image: "https://images.unsplash.com/photo-1487412912498-0447578fcca8?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Beauty"
},
{
image: "https://plus.unsplash.com/premium_photo-1673953509975-576678fa6710?q=80&w=1740&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Health"
},
{
image: "https://images.unsplash.com/photo-1458560871784-56d23406c091?q=80&w=1674&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Music"
},
{
image: "https://images.unsplash.com/photo-1488646953014-85cb44e25828?q=80&w=1635&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Travel"
},
{
image: "https://images.unsplash.com/photo-1458560871784-56d23406c091?q=80&w=1674&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Music"
},
{
image: "https://images.unsplash.com/photo-1488646953014-85cb44e25828?q=80&w=1635&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
category: "Travel"
},

];

function HeroPage() {
const dispatch = useDispatch();
const { allCategories, isCategoriesFetched } :any = useSelector((state: RootState) => state.category);
const { data: categoriess } = useGetAllCategoriesQuery();

useEffect(() => {
if (!isCategoriesFetched && categoriess) {
dispatch(setallCategories(categoriess));
dispatch(setIsCategoriesFetched(true));
}
}, [categoriess, isCategoriesFetched, dispatch]);

const categoriesToDisplay = allCategories.length ? allCategories.data : categoriess;
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",
Expand All @@ -82,7 +45,7 @@ function HeroPage() {
}, []);

return (
<div className="p-3 md:p-4 xl:px-10">
<div className="p-3 md:p-4 xl:px-10 font-roboto">
<div className="hero-container flex flex-col-reverse md:grid md:grid-cols-3 md:gap-4">
<div className="small-images grid grid-cols-2 md:flex md:flex-col md:gap-1 md:col-span-1">
<div className="upper-image relative">
Expand Down Expand Up @@ -118,11 +81,19 @@ function HeroPage() {
<h1 className='text-2xl font-bold'>Shop By Category Here:</h1>
</div>
<div className="categories flex flex-row gap-4 overflow-x-auto snap-x">
{categories.map((cat, index) => (
<div key={index} className="snap-start">
<Category image={cat.image} category={cat.category} />
</div>
))}
{list && list.length ? (
<div className="categories flex flex-row gap-4 overflow-x-auto snap-x">
{list.map((cat:{image:string, name: string}, index:number) => (
<div key={index} className="snap-start">
<Category image={cat.image} name={cat.name} />
</div>
))}
</div>
) : (
<div className="">
<CategorySkeleton />
</div>
)}
</div>
<div className="banner py-10 sm:grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-2">
<div className="smater-value flex justify-center items-center gap-2 font-bold text-xl">SMARTER VALUES, GREAT DEALS</div>
Expand Down
34 changes: 21 additions & 13 deletions src/containers/nav/NavbarComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import education from '../../assets/education.png';
import suppliment from '../../assets/supplement.png';
import shoes from '../../assets/shoes.png';
import electronics from '../../assets/electronics.png';
import { Link, NavLink } from 'react-router-dom';
import { useNavigate, NavLink } from 'react-router-dom';

export function PopularCategory({ title }: { title: string }) {

export function PopularCategory({ title }: { title: string }) {
return (
<>
<div className='p-3'>
Expand Down Expand Up @@ -35,28 +36,35 @@ export function SidebarLink({ icon, name, to }: { icon: string; name: string; to
);
}
export function DesktopNav() {

const navigate = useNavigate();

const handleClick = (name: string) => {
navigate(`/categories/${name}`);
};

return (
<div className='bg-blackColor text-whiteColor hidden md:block w-full'>
<div className='flex justify-center items-center border-whiteColor'>
<DeskNavLink to='/' name='Plus' />
<DeskNavLink to='/' name='Flash Sales' />
<DeskNavLink to='/' name='Babies' />
<DeskNavLink to='/' name='Fathers' />
<DeskNavLink to='/' name='Electronics' />
<DeskNavLink to='/' name='Beauty' />
<DeskNavLink to='/' name='Sports' />
<DeskNavLink name='Plus' onClick={() => handleClick('Plus')} />
<DeskNavLink name='Flash Sales' onClick={() => handleClick('Flash Sales')} />
<DeskNavLink name='Babies' onClick={() => handleClick('Babies')} />
<DeskNavLink name='Fathers' onClick={() => handleClick('Fathers')} />
<DeskNavLink name='Electronics' onClick={() => handleClick('Electronics')} />
<DeskNavLink name='Beauty' onClick={() => handleClick('Beauty')} />
<DeskNavLink name='Sports' onClick={() => handleClick('Sports')} />
</div>
</div>
);
}

function DeskNavLink({ to, name }: { to: string; name: string }) {
function DeskNavLink({ onClick, name }: { onClick: () => void, name: string }) {
return (
<Link
to={to}
<div
onClick={onClick}
className={`px-5 py-2 cursor-pointer hover:bg-whiteColor hover:text-blackColor transition-all delay-100 ease-linear`}
>
{name}
</Link>
</div>
);
}
Loading

0 comments on commit ea71b18

Please sign in to comment.