checkpoint before checking out main
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "sanihome-tmp",
|
"name": "sanihome",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
Create a website for home appliances store
|
||||||
|
|
||||||
|
Create sample content
|
||||||
|
Lang:fa
|
||||||
|
Directions: rtl
|
||||||
|
Use next js
|
||||||
|
Color palette is red and dark red - light theme
|
||||||
|
|
||||||
|
Design only homepage
|
||||||
|
Use Persian font like iran-yekan or kalameh or iran sans and some cool English font
|
||||||
|
Do not use images - show only placeholders with sizes
|
||||||
|
|
||||||
|
Sections are:
|
||||||
|
Top header bar
|
||||||
|
Header
|
||||||
|
Slider- one slide in center and two slide beside each side be visible
|
||||||
|
Featured categories
|
||||||
|
Product Special sale carousel
|
||||||
|
Two banner
|
||||||
|
Product Top selling carousel
|
||||||
|
3 banner
|
||||||
|
Samsung brand product carousel
|
||||||
|
About sanihome
|
||||||
|
Latest blog carousel
|
||||||
|
footer
|
||||||
+68
-10
@@ -1,26 +1,84 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #fafafa;
|
||||||
--foreground: #171717;
|
--foreground: #1a1a1a;
|
||||||
|
--surface: #ffffff;
|
||||||
|
--muted: #6b7280;
|
||||||
|
--border: #e5e7eb;
|
||||||
|
--red: #dc2626;
|
||||||
|
--red-dark: #991b1b;
|
||||||
|
--red-light: #fef2f2;
|
||||||
|
--red-muted: #fca5a5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--font-sans: var(--font-geist-sans);
|
--color-surface: var(--surface);
|
||||||
--font-mono: var(--font-geist-mono);
|
--color-muted: var(--muted);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-red: var(--red);
|
||||||
|
--color-red-dark: var(--red-dark);
|
||||||
|
--color-red-light: var(--red-light);
|
||||||
|
--color-red-muted: var(--red-muted);
|
||||||
|
--font-sans: var(--font-vazirmatn);
|
||||||
|
--font-display: var(--font-syne);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
* {
|
||||||
:root {
|
box-sizing: border-box;
|
||||||
--background: #0a0a0a;
|
}
|
||||||
--foreground: #ededed;
|
|
||||||
}
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: var(--font-vazirmatn), system-ui, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-display {
|
||||||
|
font-family: var(--font-syne), var(--font-vazirmatn), sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(16px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(24px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-up {
|
||||||
|
animation: fade-up 0.6s ease-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-slide-in {
|
||||||
|
animation: slide-in 0.5s ease-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-track {
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-track::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-10
@@ -1,27 +1,30 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Vazirmatn, Syne } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const vazirmatn = Vazirmatn({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-vazirmatn",
|
||||||
subsets: ["latin"],
|
subsets: ["arabic", "latin"],
|
||||||
|
weight: ["400", "500", "600", "700"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
const syne = Syne({
|
||||||
variable: "--font-geist-mono",
|
variable: "--font-syne",
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
weight: ["600", "700", "800"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "سانیهوم | فروشگاه لوازم خانگی",
|
||||||
description: "Generated by create next app",
|
description: "فروشگاه آنلاین لوازم خانگی — یخچال، لباسشویی، تلویزیون و بیشتر",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({ children }: LayoutProps<"/">) {
|
export default function RootLayout({ children }: LayoutProps<"/">) {
|
||||||
return (
|
return (
|
||||||
<html
|
<html
|
||||||
lang="en"
|
lang="fa"
|
||||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
dir="rtl"
|
||||||
|
className={`${vazirmatn.variable} ${syne.variable} h-full antialiased`}
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col">{children}</body>
|
<body className="min-h-full flex flex-col">{children}</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+29
-63
@@ -1,69 +1,35 @@
|
|||||||
import Image from "next/image";
|
import { TopBar } from "@/components/home/TopBar";
|
||||||
|
import { Header } from "@/components/home/Header";
|
||||||
|
import { FeatureBar } from "@/components/home/FeatureBar";
|
||||||
|
import { HeroSlider } from "@/components/home/HeroSlider";
|
||||||
|
import { FeaturedCategories } from "@/components/home/FeaturedCategories";
|
||||||
|
import { SpecialSaleCarousel } from "@/components/home/SpecialSaleCarousel";
|
||||||
|
import { TwoBanners } from "@/components/home/TwoBanners";
|
||||||
|
import { TopSellingCarousel } from "@/components/home/TopSellingCarousel";
|
||||||
|
import { ThreeBanners } from "@/components/home/ThreeBanners";
|
||||||
|
import { SamsungCarousel } from "@/components/home/SamsungCarousel";
|
||||||
|
import { AboutSanihome } from "@/components/home/AboutSanihome";
|
||||||
|
import { BlogCarousel } from "@/components/home/BlogCarousel";
|
||||||
|
import { Footer } from "@/components/home/Footer";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
<>
|
||||||
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
<TopBar />
|
||||||
<Image
|
<Header />
|
||||||
className="dark:invert h-5 w-[100px]"
|
<main>
|
||||||
src="/next.svg"
|
<HeroSlider />
|
||||||
alt="Next.js logo"
|
<SpecialSaleCarousel />
|
||||||
width={100}
|
<FeatureBar />
|
||||||
height={20}
|
<FeaturedCategories />
|
||||||
priority
|
<TwoBanners />
|
||||||
/>
|
<TopSellingCarousel />
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
<ThreeBanners />
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
<SamsungCarousel />
|
||||||
To get started, edit the{" "}
|
<AboutSanihome />
|
||||||
<code className="rounded bg-black/[.06] px-1.5 py-0.5 font-mono text-[0.9em] dark:bg-white/[.08]">
|
<BlogCarousel />
|
||||||
page.tsx
|
|
||||||
</code>{" "}
|
|
||||||
file.
|
|
||||||
</h1>
|
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert h-[14px] w-4"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={14}
|
|
||||||
/>
|
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
<Footer />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
|
||||||
|
export function AboutSanihome() {
|
||||||
|
return (
|
||||||
|
<section className="border-y border-red-light bg-gradient-to-br from-red-light/40 via-surface to-background py-12 md:py-16">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<div className="grid items-center gap-8 md:grid-cols-2">
|
||||||
|
<div className="animate-fade-up">
|
||||||
|
<div className="mb-4 flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||||
|
<h2 className="text-2xl font-bold text-red-dark md:text-3xl">
|
||||||
|
درباره سانیهوم
|
||||||
|
</h2>
|
||||||
|
<span
|
||||||
|
className="font-display text-sm font-semibold tracking-wide text-red-dark/30 uppercase md:text-base"
|
||||||
|
lang="en"
|
||||||
|
dir="ltr"
|
||||||
|
>
|
||||||
|
About Sanihome
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="mb-4 leading-relaxed text-muted">
|
||||||
|
سانیهوم از سال ۱۳۹۰ به عنوان یکی از معتبرترین فروشگاههای آنلاین لوازم
|
||||||
|
خانگی در ایران فعالیت میکند. ما با ارائه محصولات اصل از برندهای
|
||||||
|
معتبر جهانی، گارانتی رسمی و خدمات پس از فروش، تجربهای مطمئن برای
|
||||||
|
خرید لوازم خانگی فراهم کردهایم.
|
||||||
|
</p>
|
||||||
|
<p className="mb-6 leading-relaxed text-muted">
|
||||||
|
تیم متخصص ما آماده مشاوره رایگان، ارسال سریع به سراسر کشور و
|
||||||
|
نصب حرفهای در محل شماست.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
{[
|
||||||
|
{ value: "+15", label: "سال تجربه" },
|
||||||
|
{ value: "+50K", label: "مشتری راضی" },
|
||||||
|
{ value: "+200", label: "برند معتبر" },
|
||||||
|
].map((stat) => (
|
||||||
|
<div key={stat.label} className="rounded-xl border border-border bg-surface p-4 text-center">
|
||||||
|
<p className="font-display text-xl font-bold tracking-tight text-red-dark" lang="en" dir="ltr">
|
||||||
|
{stat.value}
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-xs text-muted">{stat.label}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Placeholder
|
||||||
|
width="100%"
|
||||||
|
height="360px"
|
||||||
|
label="560×360"
|
||||||
|
className="w-full rounded-2xl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||||
|
import { Carousel } from "@/components/ui/Carousel";
|
||||||
|
import { blogPosts } from "@/data/sample";
|
||||||
|
|
||||||
|
export function BlogCarousel() {
|
||||||
|
return (
|
||||||
|
<section className="py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<SectionTitle
|
||||||
|
title="آخرین مطالب وبلاگ"
|
||||||
|
enTitle="Latest Blog"
|
||||||
|
action="وبلاگ"
|
||||||
|
/>
|
||||||
|
<Carousel ariaLabel="کاروسل وبلاگ">
|
||||||
|
{blogPosts.map((post) => (
|
||||||
|
<article
|
||||||
|
key={post.id}
|
||||||
|
className="w-[280px] shrink-0 overflow-hidden rounded-xl border border-border bg-surface transition-shadow hover:shadow-md md:w-[300px]"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width="100%"
|
||||||
|
height="160px"
|
||||||
|
label="300×160"
|
||||||
|
className="w-full rounded-none border-0 border-b border-dashed"
|
||||||
|
/>
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="mb-2 flex items-center gap-2 text-[11px] text-muted">
|
||||||
|
<span>{post.date}</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>{post.readTime} مطالعه</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="mb-2 line-clamp-2 text-sm font-bold leading-relaxed">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
<p className="line-clamp-2 text-xs leading-relaxed text-muted">
|
||||||
|
{post.excerpt}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mt-3 text-xs font-medium text-red transition-colors hover:text-red-dark"
|
||||||
|
>
|
||||||
|
ادامه مطلب ←
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
function TruckIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-6 w-6" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M3 7h11v10H3V7Zm11 3h4l3 3v4h-7v-7Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<circle cx="7" cy="18" r="1.5" fill="currentColor" />
|
||||||
|
<circle cx="17" cy="18" r="1.5" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShieldIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-6 w-6" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M12 3 5 6v5c0 4.5 2.8 7.8 7 9 4.2-1.2 7-4.5 7-9V6l-7-3Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="m9.5 12 1.8 1.8 3.7-3.8"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReturnIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-6 w-6" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M7 7H3v4"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.5 11A8 8 0 1 0 5 7.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReviewIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-6 w-6" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M8 4h8a2 2 0 0 1 2 2v14l-6-3-6 3V6a2 2 0 0 1 2-2Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M9.5 10h5M9.5 13h3.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
fa: "ارسال رایگان بالاتر از ۵ میلیون تومان",
|
||||||
|
en: "Free Shipping Over 50M",
|
||||||
|
Icon: TruckIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fa: "ضمانت اصالت کالا",
|
||||||
|
en: "Authenticity Guarantee",
|
||||||
|
Icon: ShieldIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fa: "امکان مرجوعی کالا",
|
||||||
|
en: "Easy Returns",
|
||||||
|
Icon: ReturnIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fa: "بررسیهای تخصصی کالا",
|
||||||
|
en: "Expert Reviews",
|
||||||
|
Icon: ReviewIcon,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function FeatureBar() {
|
||||||
|
return (
|
||||||
|
<section className="border-b border-border bg-surface">
|
||||||
|
<div className="mx-auto max-w-7xl px-4 py-4">
|
||||||
|
<div className="grid grid-cols-2 gap-3 rounded-2xl border border-border bg-background p-3 md:grid-cols-4 md:gap-0 md:divide-x md:divide-x-reverse md:divide-border md:p-0">
|
||||||
|
{features.map(({ fa, en, Icon }) => (
|
||||||
|
<div
|
||||||
|
key={en}
|
||||||
|
className="flex items-center gap-3 rounded-xl bg-surface p-3 md:rounded-none md:bg-transparent md:px-5 md:py-4"
|
||||||
|
>
|
||||||
|
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-red-light text-red">
|
||||||
|
<Icon />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="text-xs leading-relaxed font-medium text-foreground sm:text-sm">
|
||||||
|
{fa}
|
||||||
|
</p>
|
||||||
|
<p className="font-display mt-0.5 text-[10px] tracking-wide text-muted uppercase sm:text-[11px]">
|
||||||
|
{en}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||||
|
import { categories } from "@/data/sample";
|
||||||
|
|
||||||
|
export function FeaturedCategories() {
|
||||||
|
return (
|
||||||
|
<section className="py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<SectionTitle
|
||||||
|
title="دستهبندیهای پرطرفدار"
|
||||||
|
enTitle="Featured Categories"
|
||||||
|
action="همه دستهها"
|
||||||
|
/>
|
||||||
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-8">
|
||||||
|
{categories.map((cat, i) => (
|
||||||
|
<a
|
||||||
|
key={cat.id}
|
||||||
|
href="#"
|
||||||
|
className="animate-slide-in group flex flex-col items-center rounded-xl border border-border bg-surface p-3 transition-all hover:border-red hover:shadow-md"
|
||||||
|
style={{ animationDelay: `${i * 60}ms` }}
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width="72px"
|
||||||
|
height="72px"
|
||||||
|
label="72×72"
|
||||||
|
className="mb-3 rounded-full transition-colors group-hover:border-red"
|
||||||
|
/>
|
||||||
|
<span className="text-center text-xs font-medium leading-relaxed md:text-sm">
|
||||||
|
{cat.name}
|
||||||
|
</span>
|
||||||
|
<span className="mt-1 text-[11px] text-muted">{cat.count} محصول</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="border-t border-red-dark/20 bg-red-dark text-white">
|
||||||
|
<div className="mx-auto max-w-7xl px-4 py-12">
|
||||||
|
<div className="grid gap-8 md:grid-cols-4">
|
||||||
|
<div>
|
||||||
|
<div className="mb-4 flex items-center gap-2">
|
||||||
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-white text-base font-bold text-red-dark">
|
||||||
|
S
|
||||||
|
</div>
|
||||||
|
<span className="font-display text-lg font-bold tracking-tight">
|
||||||
|
SANIHOME
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm leading-relaxed text-white/70">
|
||||||
|
فروشگاه آنلاین لوازم خانگی با بیش از ۱۵ سال سابقه. محصولات اصل،
|
||||||
|
گارانتی معتبر، ارسال سریع.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="mb-3 text-sm font-bold">دسترسی سریع</h4>
|
||||||
|
<ul className="space-y-2 text-sm text-white/70">
|
||||||
|
{["درباره ما", "تماس با ما", "سوالات متداول", "قوانین و مقررات", "پیگیری سفارش"].map(
|
||||||
|
(link) => (
|
||||||
|
<li key={link}>
|
||||||
|
<a href="#" className="transition-colors hover:text-white">
|
||||||
|
{link}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="mb-3 text-sm font-bold">دستهبندیها</h4>
|
||||||
|
<ul className="space-y-2 text-sm text-white/70">
|
||||||
|
{["یخچال و فریزر", "ماشین لباسشویی", "تلویزیون", "جاروبرقی", "لوازم آشپزخانه"].map(
|
||||||
|
(link) => (
|
||||||
|
<li key={link}>
|
||||||
|
<a href="#" className="transition-colors hover:text-white">
|
||||||
|
{link}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="mb-3 text-sm font-bold">تماس با ما</h4>
|
||||||
|
<ul className="space-y-2 text-sm text-white/70">
|
||||||
|
<li>تهران، خیابان ولیعصر</li>
|
||||||
|
<li>تلفن: ۰۲۱-۸۸۰۰۱۲۳۴</li>
|
||||||
|
<li>ایمیل: info@sanihome.ir</li>
|
||||||
|
</ul>
|
||||||
|
<div className="mt-4 flex gap-3">
|
||||||
|
{["IG", "TG", "WA"].map((social) => (
|
||||||
|
<a
|
||||||
|
key={social}
|
||||||
|
href="#"
|
||||||
|
className="font-display flex h-8 w-8 items-center justify-center rounded-full border border-white/20 text-[10px] font-bold transition-colors hover:border-white hover:bg-white/10"
|
||||||
|
>
|
||||||
|
{social}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-10 flex flex-col items-center justify-between gap-3 border-t border-white/10 pt-6 text-xs text-white/50 md:flex-row">
|
||||||
|
<p>© ۱۴۰۵ سانیهوم — تمامی حقوق محفوظ است.</p>
|
||||||
|
<p className="font-display tracking-wide uppercase">
|
||||||
|
Designed for Sanihome
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { categories } from "@/data/sample";
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ label: "فروش ویژه", href: "#" },
|
||||||
|
{ label: "درباره ما", href: "#" },
|
||||||
|
{ label: "سوالات متداول", href: "#" },
|
||||||
|
{ label: "تماس با ما", href: "#" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const megaColumns = [
|
||||||
|
{
|
||||||
|
title: "سرمایش و گرمایش",
|
||||||
|
en: "Cooling & Heating",
|
||||||
|
items: ["یخچال و فریزر", "کولر گازی", "آبگرمکن", "بخاری"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "شستشو",
|
||||||
|
en: "Laundry & Care",
|
||||||
|
items: ["ماشین لباسشویی", "ماشین ظرفشویی", "خشککن", "اتو بخار"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "صوتی و تصویری",
|
||||||
|
en: "AV & Display",
|
||||||
|
items: ["تلویزیون", "ساندبار", "سینمای خانگی", "پروژکتور"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "آشپزخانه",
|
||||||
|
en: "Kitchen",
|
||||||
|
items: ["مایکروویو", "جاروبرقی", "هود", "اجاق گاز"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onClickOutside = (event: MouseEvent) => {
|
||||||
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
||||||
|
setMenuOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("mousedown", onClickOutside);
|
||||||
|
return () => document.removeEventListener("mousedown", onClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="sticky top-0 z-50 border-b border-border bg-surface/95 backdrop-blur-sm">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<div className="flex items-center justify-between gap-4 py-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-red text-lg font-bold text-white">
|
||||||
|
S
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-display text-xl font-bold tracking-tight">
|
||||||
|
<span className="text-red">SANI</span>
|
||||||
|
<span className="text-foreground">HOME</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted">فروشگاه لوازم خانگی</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden flex-1 px-6 md:block">
|
||||||
|
<div className="flex overflow-hidden rounded-xl border border-border bg-background">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
placeholder="جستجوی محصول، برند یا دستهبندی..."
|
||||||
|
className="flex-1 bg-transparent px-4 py-2.5 text-sm outline-none placeholder:text-muted"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-red px-5 text-sm font-medium text-white transition-colors hover:bg-red-dark"
|
||||||
|
>
|
||||||
|
جستجو
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3 text-sm">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="hidden items-center gap-1.5 text-muted transition-colors hover:text-red sm:flex"
|
||||||
|
>
|
||||||
|
<span>👤</span>
|
||||||
|
<span>ورود</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="relative flex items-center gap-1.5 rounded-lg border border-border px-3 py-2 transition-colors hover:border-red hover:text-red"
|
||||||
|
>
|
||||||
|
<span>🛒</span>
|
||||||
|
<span className="hidden sm:inline">سبد خرید</span>
|
||||||
|
<span className="absolute -top-1.5 -left-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-red text-[10px] font-bold text-white">
|
||||||
|
۲
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav className="relative flex items-center gap-6 overflow-visible border-t border-border py-3 text-sm">
|
||||||
|
<div
|
||||||
|
ref={menuRef}
|
||||||
|
className="relative"
|
||||||
|
onMouseEnter={() => setMenuOpen(true)}
|
||||||
|
onMouseLeave={() => setMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setMenuOpen((open) => !open)}
|
||||||
|
aria-expanded={menuOpen}
|
||||||
|
aria-haspopup="true"
|
||||||
|
className="flex shrink-0 items-center gap-2 rounded-lg bg-red px-4 py-2 font-medium text-white transition-colors hover:bg-red-dark"
|
||||||
|
>
|
||||||
|
<span aria-hidden>☰</span>
|
||||||
|
دستهبندیها
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{menuOpen && (
|
||||||
|
<div className="absolute top-full right-0 z-50 mt-0 w-[min(92vw,760px)] animate-fade-up rounded-b-2xl border border-t-0 border-border bg-surface p-5 shadow-xl">
|
||||||
|
<div className="mb-4 flex items-end justify-between gap-3 border-b border-border pb-3">
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-red-dark">همه دستهبندیها</p>
|
||||||
|
<p className="font-display text-[11px] tracking-wide text-muted uppercase">
|
||||||
|
Shop by Category
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="#" className="text-xs font-medium text-red hover:text-red-dark">
|
||||||
|
مشاهده همه ←
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
{megaColumns.map((column) => (
|
||||||
|
<div key={column.title}>
|
||||||
|
<p className="mb-0.5 text-sm font-bold text-red-dark">{column.title}</p>
|
||||||
|
<p className="font-display mb-3 text-[10px] tracking-wider text-muted uppercase">
|
||||||
|
{column.en}
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{column.items.map((item) => (
|
||||||
|
<li key={item}>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="text-sm text-muted transition-colors hover:text-red"
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid grid-cols-2 gap-2 border-t border-border pt-4 sm:grid-cols-4">
|
||||||
|
{categories.slice(0, 4).map((cat) => (
|
||||||
|
<a
|
||||||
|
key={cat.id}
|
||||||
|
href="#"
|
||||||
|
className="rounded-lg bg-red-light/50 px-3 py-2 text-center text-xs font-medium text-red-dark transition-colors hover:bg-red-light"
|
||||||
|
>
|
||||||
|
{cat.name}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{navLinks.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.label}
|
||||||
|
href={link.href}
|
||||||
|
className="shrink-0 text-muted transition-colors hover:text-red"
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
import { sliderSlides } from "@/data/sample";
|
||||||
|
|
||||||
|
export function HeroSlider() {
|
||||||
|
const [active, setActive] = useState(1);
|
||||||
|
|
||||||
|
const positions = [
|
||||||
|
{ offset: -1, scale: "scale-[0.92]", opacity: "opacity-55", z: "z-10" },
|
||||||
|
{ offset: 0, scale: "scale-100", opacity: "opacity-100", z: "z-20" },
|
||||||
|
{ offset: 1, scale: "scale-[0.92]", opacity: "opacity-55", z: "z-10" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="overflow-x-clip bg-gradient-to-b from-red-light/30 to-background py-4 md:py-6">
|
||||||
|
<div className="relative mx-auto w-full max-w-7xl px-4">
|
||||||
|
<div className="relative h-[220px] sm:h-[280px] md:h-[340px] lg:h-[380px]">
|
||||||
|
{positions.map(({ offset, scale, opacity, z }) => {
|
||||||
|
const isCenter = offset === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={offset}
|
||||||
|
className={`absolute top-1/2 -translate-y-1/2 transition-all duration-500 ease-out ${scale} ${opacity} ${z} ${
|
||||||
|
isCenter
|
||||||
|
? "inset-x-0 w-full origin-center"
|
||||||
|
: offset === -1
|
||||||
|
? "right-full mr-1.5 w-[78%] origin-right sm:w-[70%] md:w-[65%]"
|
||||||
|
: "left-full ml-1.5 w-[78%] origin-left sm:w-[70%] md:w-[65%]"
|
||||||
|
} ${isCenter ? "" : "pointer-events-none"}`}
|
||||||
|
>
|
||||||
|
<div className="overflow-hidden rounded-2xl border border-border bg-surface shadow-lg">
|
||||||
|
<Placeholder
|
||||||
|
width="100%"
|
||||||
|
label={isCenter ? "1280×380" : "880×350"}
|
||||||
|
className={`w-full rounded-none border-0 h-[200px] sm:h-[260px] md:h-[310px] ${
|
||||||
|
isCenter ? "lg:h-[380px]" : "lg:h-[350px]"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex items-center justify-center gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
setActive((prev) => (prev - 1 + sliderSlides.length) % sliderSlides.length)
|
||||||
|
}
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-full border border-border text-red transition-colors hover:border-red hover:bg-red-light"
|
||||||
|
aria-label="اسلاید قبلی"
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</button>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
{sliderSlides.map((slide, i) => (
|
||||||
|
<button
|
||||||
|
key={slide.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => setActive(i)}
|
||||||
|
aria-label={`اسلاید ${i + 1}`}
|
||||||
|
className={`h-2 rounded-full transition-all ${
|
||||||
|
i === active ? "w-8 bg-red" : "w-2 bg-red-muted/50"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setActive((prev) => (prev + 1) % sliderSlides.length)}
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-full border border-border text-red transition-colors hover:border-red hover:bg-red-light"
|
||||||
|
aria-label="اسلاید بعدی"
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Carousel } from "@/components/ui/Carousel";
|
||||||
|
import { ProductCard } from "@/components/ui/ProductCard";
|
||||||
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||||
|
import { samsungProducts } from "@/data/sample";
|
||||||
|
|
||||||
|
export function SamsungCarousel() {
|
||||||
|
return (
|
||||||
|
<section className="py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<SectionTitle
|
||||||
|
title="محصولات سامسونگ"
|
||||||
|
enTitle="Samsung Collection"
|
||||||
|
action="مشاهده همه"
|
||||||
|
/>
|
||||||
|
<Carousel ariaLabel="کاروسل محصولات سامسونگ">
|
||||||
|
{samsungProducts.map((product) => (
|
||||||
|
<ProductCard
|
||||||
|
key={product.id}
|
||||||
|
name={product.name}
|
||||||
|
brand={product.brand}
|
||||||
|
price={product.price}
|
||||||
|
variant="brand"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Carousel } from "@/components/ui/Carousel";
|
||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
import { ProductCard } from "@/components/ui/ProductCard";
|
||||||
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||||
|
import { specialSaleProducts } from "@/data/sample";
|
||||||
|
|
||||||
|
function SpecialSaleBanner() {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="relative w-[220px] shrink-0 self-stretch overflow-hidden rounded-xl border border-border md:w-[240px]"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width="100%"
|
||||||
|
label="240×380"
|
||||||
|
className="h-full min-h-[380px] w-full rounded-none border-0"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SpecialSaleCarousel() {
|
||||||
|
return (
|
||||||
|
<section className="border-y border-red-light bg-red-light/30 py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<SectionTitle
|
||||||
|
title="فروش ویژه"
|
||||||
|
enTitle="Special Sale"
|
||||||
|
action="مشاهده همه"
|
||||||
|
/>
|
||||||
|
<Carousel ariaLabel="کاروسل فروش ویژه">
|
||||||
|
<SpecialSaleBanner />
|
||||||
|
{specialSaleProducts.map((product) => (
|
||||||
|
<ProductCard
|
||||||
|
key={product.id}
|
||||||
|
name={product.name}
|
||||||
|
brand={product.brand}
|
||||||
|
price={product.price}
|
||||||
|
oldPrice={product.oldPrice}
|
||||||
|
discount={product.discount}
|
||||||
|
variant="sale"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
|
||||||
|
const banners = [
|
||||||
|
{ title: "ماشین لباسشویی", subtitle: "از ۲۸ میلیون", label: "420×160" },
|
||||||
|
{ title: "جاروبرقی", subtitle: "ارسال فوری", label: "420×160" },
|
||||||
|
{ title: "لوازم آشپزخانه", subtitle: "تا ۲۰٪ تخفیف", label: "420×160" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function ThreeBanners() {
|
||||||
|
return (
|
||||||
|
<section className="border-y border-border bg-background py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
|
{banners.map((banner) => (
|
||||||
|
<a
|
||||||
|
key={banner.title}
|
||||||
|
href="#"
|
||||||
|
className="group relative overflow-hidden rounded-xl border border-border bg-surface transition-all hover:border-red hover:shadow-md"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width="100%"
|
||||||
|
height="160px"
|
||||||
|
label={banner.label}
|
||||||
|
className="w-full rounded-none border-0"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex flex-col items-start justify-end bg-gradient-to-t from-black/60 to-transparent p-5">
|
||||||
|
<h3 className="font-bold text-white">{banner.title}</h3>
|
||||||
|
<p className="text-sm text-white/75">{banner.subtitle}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export function TopBar() {
|
||||||
|
return (
|
||||||
|
<div className="border-b border-red-dark/10 bg-red-dark text-white">
|
||||||
|
<div className="mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-2 px-4 py-2 text-xs md:text-sm">
|
||||||
|
<p>ارسال رایگان برای سفارشهای بالای ۵ میلیون تومان</p>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<span>پشتیبانی: ۰۲۱-۸۸۰۰۱۲۳۴</span>
|
||||||
|
<span className="hidden sm:inline">|</span>
|
||||||
|
<span className="hidden sm:inline">شنبه تا پنجشنبه ۹ تا ۱۸</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Carousel } from "@/components/ui/Carousel";
|
||||||
|
import { ProductCard } from "@/components/ui/ProductCard";
|
||||||
|
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||||
|
import { topSellingProducts } from "@/data/sample";
|
||||||
|
|
||||||
|
export function TopSellingCarousel() {
|
||||||
|
return (
|
||||||
|
<section className="py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<SectionTitle
|
||||||
|
title="پرفروشترینها"
|
||||||
|
enTitle="Top Selling"
|
||||||
|
action="مشاهده همه"
|
||||||
|
/>
|
||||||
|
<Carousel ariaLabel="کاروسل پرفروشترینها">
|
||||||
|
{topSellingProducts.map((product) => (
|
||||||
|
<ProductCard
|
||||||
|
key={product.id}
|
||||||
|
name={product.name}
|
||||||
|
brand={product.brand}
|
||||||
|
price={product.price}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Placeholder } from "@/components/ui/Placeholder";
|
||||||
|
|
||||||
|
export function TwoBanners() {
|
||||||
|
return (
|
||||||
|
<section className="py-10 md:py-14">
|
||||||
|
<div className="mx-auto max-w-7xl px-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="group relative overflow-hidden rounded-2xl border border-border bg-surface transition-shadow hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<Placeholder width="100%" height="200px" label="640×200" className="w-full rounded-none border-0" />
|
||||||
|
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-red-dark/80 to-transparent p-6">
|
||||||
|
<p className="font-display text-xs tracking-widest text-red-muted uppercase">
|
||||||
|
Summer Sale
|
||||||
|
</p>
|
||||||
|
<h3 className="text-lg font-bold text-white">تخفیف ویژه یخچال و فریزر</h3>
|
||||||
|
<p className="mt-1 text-sm text-white/80">تا ۲۵٪ تخفیف — تا پایان مرداد</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="group relative overflow-hidden rounded-2xl border border-border bg-surface transition-shadow hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<Placeholder width="100%" height="200px" label="640×200" className="w-full rounded-none border-0" />
|
||||||
|
<div className="absolute inset-0 flex flex-col justify-end bg-gradient-to-t from-red-dark/80 to-transparent p-6">
|
||||||
|
<p className="font-display text-xs tracking-widest text-red-muted uppercase">
|
||||||
|
Smart TV
|
||||||
|
</p>
|
||||||
|
<h3 className="text-lg font-bold text-white">تلویزیونهای هوشمند ۲۰۲۶</h3>
|
||||||
|
<p className="mt-1 text-sm text-white/80">QLED و OLED با اقساط ۱۲ ماهه</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRef } from "react";
|
||||||
|
|
||||||
|
type CarouselProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
ariaLabel: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ChevronLeftIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-5 w-5" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M14.5 6.5 9 12l5.5 5.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChevronRightIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" className="h-5 w-5" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M9.5 6.5 15 12l-5.5 5.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Carousel({ children, ariaLabel }: CarouselProps) {
|
||||||
|
const trackRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const scroll = (direction: "prev" | "next") => {
|
||||||
|
const track = trackRef.current;
|
||||||
|
if (!track) return;
|
||||||
|
const amount = direction === "next" ? -280 : 280;
|
||||||
|
track.scrollBy({ left: amount, behavior: "smooth" });
|
||||||
|
};
|
||||||
|
|
||||||
|
const btnClass =
|
||||||
|
"absolute top-1/2 z-10 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full border border-white/40 bg-white/40 text-red shadow-sm backdrop-blur-md transition-colors hover:border-red/40 hover:bg-white/60 hover:text-red-dark";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => scroll("prev")}
|
||||||
|
aria-label="قبلی"
|
||||||
|
className={`${btnClass} right-1`}
|
||||||
|
>
|
||||||
|
<ChevronRightIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => scroll("next")}
|
||||||
|
aria-label="بعدی"
|
||||||
|
className={`${btnClass} left-1`}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon />
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
ref={trackRef}
|
||||||
|
role="region"
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
className="carousel-track flex items-stretch gap-4 overflow-x-auto scroll-smooth px-1 py-2"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
type PlaceholderProps = {
|
||||||
|
width: string;
|
||||||
|
height?: string;
|
||||||
|
label?: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Placeholder({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
label,
|
||||||
|
className = "",
|
||||||
|
}: PlaceholderProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative flex shrink-0 items-center justify-center overflow-hidden border border-dashed border-red-muted/60 bg-red-light/40 ${className}`}
|
||||||
|
style={{ width, ...(height ? { height } : {}) }}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-[linear-gradient(135deg,transparent_45%,rgba(220,38,38,0.06)_50%,transparent_55%)]" />
|
||||||
|
<span className="font-display relative z-10 text-xs tracking-wider text-red-dark/50 uppercase">
|
||||||
|
{label ?? `${width} × ${height ?? ""}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
import { formatPrice } from "@/data/sample";
|
||||||
|
|
||||||
|
type ProductCardProps = {
|
||||||
|
name: string;
|
||||||
|
brand: string;
|
||||||
|
price: number;
|
||||||
|
oldPrice?: number;
|
||||||
|
discount?: number;
|
||||||
|
variant?: "sale" | "default" | "brand";
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProductCard({
|
||||||
|
name,
|
||||||
|
brand,
|
||||||
|
price,
|
||||||
|
oldPrice,
|
||||||
|
discount,
|
||||||
|
variant = "default",
|
||||||
|
}: ProductCardProps) {
|
||||||
|
return (
|
||||||
|
<article className="group w-[220px] shrink-0 overflow-hidden rounded-xl border border-border bg-surface transition-shadow hover:shadow-md md:w-[240px]">
|
||||||
|
<div className="relative">
|
||||||
|
<Placeholder width="100%" height="200px" label="240×200" className="w-full rounded-none border-0 border-b border-dashed" />
|
||||||
|
{discount && (
|
||||||
|
<span className="absolute top-3 right-3 rounded-md bg-red px-2 py-0.5 text-xs font-bold text-white">
|
||||||
|
{discount}٪
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{variant === "brand" && (
|
||||||
|
<span className="font-display absolute top-3 left-3 text-[10px] font-bold tracking-widest text-red-dark uppercase">
|
||||||
|
{brand}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="p-4">
|
||||||
|
<p className="font-display mb-1 text-[11px] tracking-wide text-muted uppercase">
|
||||||
|
{brand}
|
||||||
|
</p>
|
||||||
|
<h3 className="mb-3 line-clamp-2 min-h-[2.75rem] text-sm leading-relaxed font-medium">
|
||||||
|
{name}
|
||||||
|
</h3>
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<span className="text-base font-bold text-red-dark">
|
||||||
|
{formatPrice(price)}
|
||||||
|
<span className="mr-0.5 text-xs font-normal text-muted">تومان</span>
|
||||||
|
</span>
|
||||||
|
{oldPrice && (
|
||||||
|
<span className="text-xs text-muted line-through">
|
||||||
|
{formatPrice(oldPrice)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="mt-3 w-full rounded-lg bg-red py-2 text-sm font-medium text-white transition-colors hover:bg-red-dark"
|
||||||
|
>
|
||||||
|
افزودن به سبد
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
type SectionTitleProps = {
|
||||||
|
title: string;
|
||||||
|
enTitle: string;
|
||||||
|
subtitle?: string;
|
||||||
|
action?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SectionTitle({ title, enTitle, subtitle, action }: SectionTitleProps) {
|
||||||
|
return (
|
||||||
|
<div className="mb-6 flex items-end justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||||
|
<h2 className="text-xl font-bold text-red-dark md:text-2xl">{title}</h2>
|
||||||
|
<span
|
||||||
|
className="font-display text-sm font-semibold tracking-wide text-red-dark/30 uppercase md:text-base"
|
||||||
|
lang="en"
|
||||||
|
dir="ltr"
|
||||||
|
>
|
||||||
|
{enTitle}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{subtitle && (
|
||||||
|
<p className="mt-1 text-sm text-muted">{subtitle}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{action && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="shrink-0 text-sm font-medium text-red transition-colors hover:text-red-dark"
|
||||||
|
>
|
||||||
|
{action} ←
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
export const categories = [
|
||||||
|
{ id: 1, name: "یخچال و فریزر", count: 48 },
|
||||||
|
{ id: 2, name: "ماشین لباسشویی", count: 36 },
|
||||||
|
{ id: 3, name: "تلویزیون", count: 52 },
|
||||||
|
{ id: 4, name: "جاروبرقی", count: 29 },
|
||||||
|
{ id: 5, name: "مایکروویو", count: 18 },
|
||||||
|
{ id: 6, name: "ظرفشویی", count: 22 },
|
||||||
|
{ id: 7, name: "کولر و گرمایش", count: 31 },
|
||||||
|
{ id: 8, name: "لوازم آشپزخانه", count: 64 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const specialSaleProducts = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "یخچال فریزر ساید بای ساید سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 89500000,
|
||||||
|
oldPrice: 102000000,
|
||||||
|
discount: 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "ماشین لباسشویی ۹ کیلویی الجی",
|
||||||
|
brand: "LG",
|
||||||
|
price: 42800000,
|
||||||
|
oldPrice: 48500000,
|
||||||
|
discount: 11,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "تلویزیون ۵۵ اینچ QLED سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 56200000,
|
||||||
|
oldPrice: 62000000,
|
||||||
|
discount: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "جاروبرقی رباتیک شیائومی",
|
||||||
|
brand: "Xiaomi",
|
||||||
|
price: 18900000,
|
||||||
|
oldPrice: 23500000,
|
||||||
|
discount: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "مایکروویو ۳۰ لیتری بوش",
|
||||||
|
brand: "Bosch",
|
||||||
|
price: 12400000,
|
||||||
|
oldPrice: 14800000,
|
||||||
|
discount: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "ظرفشویی ۱۴ نفره بوش",
|
||||||
|
brand: "Bosch",
|
||||||
|
price: 35600000,
|
||||||
|
oldPrice: 39900000,
|
||||||
|
discount: 10,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const topSellingProducts = [
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
name: "یخچال دوقلو الجی",
|
||||||
|
brand: "LG",
|
||||||
|
price: 67800000,
|
||||||
|
sold: 124,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
name: "تلویزیون ۶۵ اینچ OLED الجی",
|
||||||
|
brand: "LG",
|
||||||
|
price: 98500000,
|
||||||
|
sold: 89,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
name: "ماشین لباسشویی ۸ کیلویی سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 38900000,
|
||||||
|
sold: 156,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
name: "کولر گازی ۲۴ هزار اینورتر",
|
||||||
|
brand: "Gree",
|
||||||
|
price: 24500000,
|
||||||
|
sold: 203,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
name: "اجاق گاز رومیزی بوش",
|
||||||
|
brand: "Bosch",
|
||||||
|
price: 16800000,
|
||||||
|
sold: 67,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
name: "جاروبرقی سایکلون سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 8900000,
|
||||||
|
sold: 178,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const samsungProducts = [
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
name: "یخچال Bespoke سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 112000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
name: "ماشین لباسشویی AI EcoBubble",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 45600000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 15,
|
||||||
|
name: "تلویزیون Neo QLED 8K",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 185000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 16,
|
||||||
|
name: "جاروبرقی Jet ۹۰",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 21500000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 17,
|
||||||
|
name: "مایکروویو Grill سامسونگ",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 13800000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 18,
|
||||||
|
name: "کولر گازی WindFree",
|
||||||
|
brand: "Samsung",
|
||||||
|
price: 32800000,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const blogPosts = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "راهنمای خرید یخچال مناسب برای خانواده",
|
||||||
|
excerpt: "نکات مهم برای انتخاب یخچال با توجه به فضای آشپزخانه و تعداد اعضای خانواده.",
|
||||||
|
date: "۱۲ مرداد ۱۴۰۵",
|
||||||
|
readTime: "۵ دقیقه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "مقایسه تلویزیون QLED و OLED",
|
||||||
|
excerpt: "تفاوتهای فنی و کاربردی دو تکنولوژی محبوب نمایشگر در سال ۱۴۰۵.",
|
||||||
|
date: "۸ مرداد ۱۴۰۵",
|
||||||
|
readTime: "۷ دقیقه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "چگونه مصرف برق لوازم خانگی را کاهش دهیم؟",
|
||||||
|
excerpt: "راهکارهای عملی برای صرفهجویی در قبض برق با استفاده هوشمند از لوازم برقی.",
|
||||||
|
date: "۳ مرداد ۱۴۰۵",
|
||||||
|
readTime: "۴ دقیقه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "معرفی جدیدترین محصولات سامسونگ ۲۰۲۶",
|
||||||
|
excerpt: "نگاهی به سری Bespoke و Neo QLED در نمایشگاه لوازم خانگی امسال.",
|
||||||
|
date: "۲۸ تیر ۱۴۰۵",
|
||||||
|
readTime: "۶ دقیقه",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const sliderSlides = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "جشنواره تابستانه لوازم خانگی",
|
||||||
|
subtitle: "تا ۳۰٪ تخفیف روی برندهای منتخب",
|
||||||
|
cta: "مشاهده پیشنهادها",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "سری جدید Samsung Bespoke",
|
||||||
|
subtitle: "طراحی هوشمند برای آشپزخانه مدرن",
|
||||||
|
cta: "کاوش مجموعه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "ارسال رایگان به سراسر کشور",
|
||||||
|
subtitle: "برای سفارشهای بالای ۵ میلیون تومان",
|
||||||
|
cta: "خرید کنید",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function formatPrice(price: number): string {
|
||||||
|
return new Intl.NumberFormat("fa-IR").format(price);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user