Amir Hossein Moghiseh 2025-02-19 16:11:53 +03:30
parent 58051d3fcf
commit d6e2dbf8da
8 changed files with 174 additions and 296 deletions

View File

@ -1,4 +1,13 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {}; const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "storage.adhorizonintl.com",
}
],
},
};
export default nextConfig; export default nextConfig;

View File

@ -9,6 +9,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^8.5.2", "embla-carousel-react": "^8.5.2",
"framer-motion": "^12.4.2", "framer-motion": "^12.4.2",
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",

View File

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
embla-carousel-autoplay:
specifier: ^8.5.2
version: 8.5.2(embla-carousel@8.5.2)
embla-carousel-react: embla-carousel-react:
specifier: ^8.5.2 specifier: ^8.5.2
version: 8.5.2(react@19.0.0) version: 8.5.2(react@19.0.0)
@ -1105,6 +1108,14 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true dev: true
/embla-carousel-autoplay@8.5.2(embla-carousel@8.5.2):
resolution: {integrity: sha512-27emJ0px3q/c0kCHCjwRrEbYcyYUPfGO3g5IBWF1i7714TTzE6L9P81V6PHLoSMAKJ1aHoT2e7YFOsuFKCbyag==}
peerDependencies:
embla-carousel: 8.5.2
dependencies:
embla-carousel: 8.5.2
dev: false
/embla-carousel-react@8.5.2(react@19.0.0): /embla-carousel-react@8.5.2(react@19.0.0):
resolution: {integrity: sha512-Tmx+uY3MqseIGdwp0ScyUuxpBgx5jX1f7od4Cm5mDwg/dptEiTKf9xp6tw0lZN2VA9JbnVMl/aikmbc53c6QFA==} resolution: {integrity: sha512-Tmx+uY3MqseIGdwp0ScyUuxpBgx5jX1f7od4Cm5mDwg/dptEiTKf9xp6tw0lZN2VA9JbnVMl/aikmbc53c6QFA==}
peerDependencies: peerDependencies:

View File

@ -1,7 +1,8 @@
"use client";
import graphql from "src/utils/graphql";
import Landing from "src/view/Landing"; import Landing from "src/view/Landing";
export default function Home() {
export default async function Home() {
return <Landing />; return <Landing />;
} }

View File

@ -1,77 +1,62 @@
"use client"; "use client";
import React, { useContext } from "react";
import logo from "../../../assets/images/logo.png"; import logo from "../../../assets/images/logo.png";
import Image from "next/image"; import Image from "next/image";
import test1 from "../../../assets/images/product/1.png";
import test2 from "../../../assets/images/product/2.png";
import test3 from "../../../assets/images/product/3.png";
import test4 from "../../../assets/images/product/4.png";
import Link from "next/link"; import Link from "next/link";
const CardNormal = ({ data, priority }) => { const CardNormal = ({ product, priority }) => {
return ( return (
<div className="group"> <div className="group">
{" "} {" "}
<> <>
<Link href={`/products/${data.id}`} onClick={(e)=>e.preventDefault()} > <Link href={`/products/${product.slug}`} onClick={(e) => e.preventDefault()} >
<div <div
className={` tr03 py-2 overflow-hidden xs:h-[270px] lg:h-[270px] border border-gray-200 ${1 == 1 ? "bg-white rounded-xl rounded-tl-[40px]" : " opacity-70" className={`tr03 py-2 overflow-hidden xs:h-[270px] lg:h-[270px] border border-gray-200 ${1 == 1 ? "bg-white rounded-xl rounded-tl-[40px]" : " opacity-70"
}`} }`}
> >
<div className="w-full h-fit flex justify-center "> <div className="w-full h-full flex justify-center relative p-2 ">
{!!data.mainImage ? ( {!!product?.images?.[0]?.url ? (
<Image <Image
src={ src={
data.mainImage == "1" product?.images?.[0]?.url
? test1
: data.mainImage == "2"
? test2
: data.mainImage == "3"
? test3
: data.mainImage == "4"
? test4
: test1
} }
width={200} fill
height={200} className="object-contain"
className="xs:!w-[110px] lg:!w-[130px] mx-auto"
priority={!!priority} priority={!!priority}
alt={`${data.persianName} - ${data.englishName}`} alt={`${product?.images?.[0]?.alternativeText}`}
/> />
) : ( ) : (
<div className="xs:!w-[85px] lg:!w-[85px] h-[90px] xs:mt-5 lg:mt-10 "> <div className="xs:!w-[85px] lg:!w-[85px] h-[90px] xs:mt-5 lg:mt-10 ">
<Image <Image
src={logo} src={logo}
className="xs:!w-[70px] lg:!w-[70px] mx-auto opacity-25 mt-5" className="xs:!w-[70px] lg:!w-[70px] mx-auto opacity-25 mt-5"
alt="وسمه"
/> />
</div> </div>
)} )}
</div> </div>
<div className="p-3 text-left"> <div className="p-3 text-left">
<p className="mb-0 text-[16px] max-h-[44px] overflow-hidden font-bold ">
{data.englishName}
</p>
<p className="mb-0 text-[13px] text-gray-600 mt-2">
{data.description}
</p>
</div> </div>
</div> </div>
</Link> </Link>
</> </>
<div className="relative"> <div className="relative">
<div className="absolute w-full bottom-0 "> <div className="absolute w-full bottom-0 ">
<div className="bg-gray-100 rounded-t-3xl flex ltr mt-2 border border-gray-100 p-2 items-center"> <div className="bg-gray-100 rounded-t-3xl ltr mt-2 border border-gray-100 p-2 flex flex-col gap-1">
<div className="flex w-full"> <p className="mb-0 text-base text-left">
{product.title}
</p>
{/* <div className="flex w-full">
<p className="mb-0 text-base ">${data.cost.toLocaleString()}</p> <p className="mb-0 text-base ">${data.cost.toLocaleString()}</p>
</div> </div> */}
<div className="w-full text-right rounded-full pr-2"> <div className="w-full text-right rounded-full pr-2">
<p className="mb-0 text-base rounded-lg underline">Detail</p> <p className="mb-0 text-sm rounded-lg text-left">{product.category.title} - {product.brand.title}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,11 +1,22 @@
"use client" "use client"
import { useCallback } from "react"
import useEmblaCarousel from "embla-carousel-react" import useEmblaCarousel from "embla-carousel-react"
import { ChevronLeft, ChevronRight } from "lucide-react" import { ChevronLeft, ChevronRight } from "lucide-react"
import { useCallback } from "react"
import CardNormal from "../Cards/CardNormal/page" import CardNormal from "../Cards/CardNormal/page"
import Autoplay from "embla-carousel-autoplay"
export function ProductCarouselSection({products}) {
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: false, align: "start" })
export default function ProductCarousel({ title, subtitle, products }) {
const [emblaRef, emblaApi] = useEmblaCarousel(
{ loop: true, align: "start" },
[
Autoplay({ delay: 3000, stopOnInteraction: true }),
]
)
const scrollPrev = useCallback(() => { const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev() if (emblaApi) emblaApi.scrollPrev()
@ -18,8 +29,8 @@ export function ProductCarouselSection({products}) {
return ( return (
<section className="py-12 px-4 md:px-6 lg:px-8"> <section className="py-12 px-4 md:px-6 lg:px-8">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-2">Our Featured Products</h2> <h2 className="text-3xl font-bold text-center mb-2">{title}</h2>
<p className="text-xl text-center text-gray-600 mb-8">Discover our handpicked selection of top-quality items</p> <p className="text-xl text-center text-gray-600 mb-8">{subtitle}</p>
<div className="relative"> <div className="relative">
<div className="overflow-hidden" ref={emblaRef}> <div className="overflow-hidden" ref={emblaRef}>
@ -29,7 +40,7 @@ export function ProductCarouselSection({products}) {
key={product.id} key={product.id}
className="flex-[0_0_100%] min-w-0 sm:flex-[0_0_50%] md:flex-[0_0_33.33%] lg:flex-[0_0_25%] pl-4" className="flex-[0_0_100%] min-w-0 sm:flex-[0_0_50%] md:flex-[0_0_33.33%] lg:flex-[0_0_25%] pl-4"
> >
<CardNormal {...product} /> <CardNormal product={product} priority={true} />
</div> </div>
))} ))}
</div> </div>
@ -37,7 +48,7 @@ export function ProductCarouselSection({products}) {
<button <button
variant="outline" variant="outline"
size="icon" size="icon"
className="absolute top-1/2 left-4 transform -translate-y-1/2" className="absolute top-1/2 left-4 transform -translate-y-1/2 bg-gray-200 rounded-full size-4"
onClick={scrollPrev} onClick={scrollPrev}
> >
<ChevronLeft className="h-4 w-4" /> <ChevronLeft className="h-4 w-4" />
@ -45,7 +56,7 @@ export function ProductCarouselSection({products}) {
<button <button
variant="outline" variant="outline"
size="icon" size="icon"
className="absolute top-1/2 right-4 transform -translate-y-1/2" className="absolute top-1/2 right-4 transform -translate-y-1/2 bg-gray-200 rounded-full size-4"
onClick={scrollNext} onClick={scrollNext}
> >
<ChevronRight className="h-4 w-4" /> <ChevronRight className="h-4 w-4" />
@ -54,4 +65,5 @@ export function ProductCarouselSection({products}) {
</div> </div>
</section> </section>
) )
} }

View File

@ -1,207 +1,11 @@
import Link from "next/link"; import Link from "next/link";
import React from "react"; import React from "react";
import CardNormal from "src/components/Cards/CardNormal/page"; import CardNormal from "src/components/Cards/CardNormal/page";
import ProductCarousel from "src/components/Carousel/ProductCarousel";
const Products = () => { const Products = ({ products }) => {
const data = [
{
id: 1,
persianName: "Hydrating Cream",
englishName: "Hydrating Cream",
description:
"A deeply moisturizing cream that keeps your skin hydrated all day long.",
cost: 250000,
costWithDiscount: 200000,
hasDiscount: true,
discountPercent: 20,
stock: 5,
mainImage: "4",
},
{
id: 2,
persianName: "Hair Strengthening Shampoo",
englishName: "Hair Strengthening Shampoo",
description:
"A nourishing shampoo that strengthens hair roots and prevents hair fall.",
cost: 180000,
costWithDiscount: 150000,
hasDiscount: true,
discountPercent: 17,
stock: 2,
mainImage: "3",
},
{
id: 3,
persianName: "Vitamin C Serum",
englishName: "Vitamin C Serum",
description:
"An antioxidant-rich serum that brightens skin and reduces signs of aging.",
cost: 300000,
costWithDiscount: 270000,
hasDiscount: true,
discountPercent: 10,
stock: 3,
mainImage: "1",
},
{
id: 4,
persianName: "Charcoal Face Mask",
englishName: "Charcoal Face Mask",
description:
"A detoxifying mask that removes impurities and unclogs pores for a fresh look.",
cost: 220000,
costWithDiscount: 220000,
hasDiscount: false,
discountPercent: 0,
stock: 8,
mainImage: "4",
},
{
id: 5,
persianName: "Body Lotion",
englishName: "Body Lotion",
description:
"A lightweight body lotion that nourishes and hydrates dry skin.",
cost: 210000,
costWithDiscount: 185000,
hasDiscount: true,
discountPercent: 12,
stock: 6,
mainImage: "2",
},
{
id: 6,
persianName: "Aloe Vera Gel",
englishName: "Aloe Vera Gel",
description:
"A soothing gel enriched with aloe vera to calm irritated skin.",
cost: 160000,
costWithDiscount: 160000,
hasDiscount: false,
discountPercent: 0,
stock: 10,
mainImage: "3",
},
{
id: 7,
persianName: "Sunscreen SPF 50",
englishName: "Sunscreen SPF 50",
description:
"A broad-spectrum sunscreen that protects against UV rays and prevents sunburn.",
cost: 280000,
costWithDiscount: 230000,
hasDiscount: true,
discountPercent: 18,
stock: 4,
mainImage: "1",
},
{
id: 8,
persianName: "Face Cleanser",
englishName: "Face Cleanser",
description:
"A gentle face cleanser that removes dirt and oil without stripping moisture.",
cost: 190000,
costWithDiscount: 170000,
hasDiscount: true,
discountPercent: 10,
stock: 7,
mainImage: "2",
},
{
id: 9,
persianName: "Moisturizing Cream",
englishName: "Moisturizing Cream",
description:
"A rich cream that provides deep hydration for soft and smooth skin.",
cost: 260000,
costWithDiscount: 260000,
hasDiscount: false,
discountPercent: 0,
stock: 9,
mainImage: "4",
},
{
id: 10,
persianName: "Eye Serum",
englishName: "Eye Serum",
description:
"A lightweight eye serum that reduces puffiness and dark circles.",
cost: 350000,
costWithDiscount: 310000,
hasDiscount: true,
discountPercent: 12,
stock: 5,
mainImage: "3",
},
{
id: 11,
persianName: "Lip Balm",
englishName: "Lip Balm",
description:
"A moisturizing lip balm that prevents chapped lips and adds a subtle shine.",
cost: 90000,
costWithDiscount: 80000,
hasDiscount: true,
discountPercent: 11,
stock: 12,
mainImage: "1",
},
{
id: 12,
persianName: "Hand Cream",
englishName: "Hand Cream",
description:
"A fast-absorbing hand cream that keeps hands soft and hydrated.",
cost: 170000,
costWithDiscount: 150000,
hasDiscount: true,
discountPercent: 12,
stock: 6,
mainImage: "2",
},
{
id: 13,
persianName: "Night Repair Serum",
englishName: "Night Repair Serum",
description:
"A serum that works overnight to repair and rejuvenate your skin.",
cost: 390000,
costWithDiscount: 350000,
hasDiscount: true,
discountPercent: 10,
stock: 4,
mainImage: "3",
},
{
id: 14,
persianName: "Shaving Cream",
englishName: "Shaving Cream",
description:
"A rich shaving cream that provides a smooth and irritation-free shave.",
cost: 200000,
costWithDiscount: 200000,
hasDiscount: false,
discountPercent: 0,
stock: 9,
mainImage: "4",
},
{
id: 15,
persianName: "Shaving ",
englishName: "Shaving ",
description:
"A fast-absorbing hand cream that keeps hands soft and hydrated.",
cost: 200000,
costWithDiscount: 200000,
hasDiscount: false,
discountPercent: 0,
stock: 9,
mainImage: "1",
},
];
console.log(products)
return ( return (
<div className="my-[120px]"> <div className="my-[120px]">
<div className="xs:px-3 md:px-10 md:container md:mx-auto mb-10"> <div className="xs:px-3 md:px-10 md:container md:mx-auto mb-10">
@ -214,13 +18,22 @@ const Products = () => {
</p> </p>
</div> </div>
<div className="grid xs:grid-cols-2 lg:grid-cols-5 gap-5">
{data?.map((e, index) => ( <div>
<ProductCarousel products={products.filter(p => p.brand.title === "active")} subtitle={""} title={"Active Products"} />
</div>
<div>
<ProductCarousel products={products.filter(p => p.brand.title === "savin")} subtitle={""} title={"Savin Products"} />
</div>
{/* <div className="grid grid-cols-1 lg:grid-cols-3 gap-5">
{products?.map((product, index) => (
<div key={index} className="relative"> <div key={index} className="relative">
<CardNormal data={e} priority /> <CardNormal product={product} priority />
</div> </div>
))} ))}
</div> </div> */}
{/* <div className="flex justify-center"> {/* <div className="flex justify-center">
<Link href={"categories/Product-20Listing-Page"}> <Link href={"categories/Product-20Listing-Page"}>

View File

@ -3,17 +3,63 @@ import AboutUs from "./components/AboutUs";
import Footer from "./components/Footer"; import Footer from "./components/Footer";
import Products from "./components/Products"; import Products from "./components/Products";
import Sides from "./components/Sides"; import Sides from "./components/Sides";
import graphql from "src/utils/graphql";
const Landing = () => { const gql = `
query Products_connection($locale: I18NLocaleCode, $page: Int, $pageSize: Int) {
products_connection(
pagination: { page: $page, pageSize: $pageSize }
locale: $locale,
sort: ["createdAt:asc"]
) {
nodes {
title
documentId
images {
alternativeText
documentId
url
}
category {
documentId
title
slug
}
brand {
title
documentId
slug
}
slug
}
}
}
`
const getProducts = async () => {
const products = await graphql(gql, {
page: 1,
pageSize: 20,
locale: "en"
})
return products.products_connection.nodes;
}
const Landing = async () => {
const products = await getProducts()
console.log(products)
return ( return (
<div className=" text-center text-6xl"> <div className=" text-center text-6xl">
{" "} {" "}
<Navbar theme={1} /> <Navbar theme={1} />
{/* <HeroSection /> */} {/* <HeroSection /> */}
<AboutUs /> <AboutUs />
<Sides/> <Sides />
{/* <CounterDetail /> */} {/* <CounterDetail /> */}
<Products /> <Products products={products} />
{/* <WhyHorizon/> */} {/* <WhyHorizon/> */}
<Footer /> <Footer />
</div> </div>