Add Nini Naderi Persian RTL homepage.
Build the kids clothing store landing page with sample content, placeholders, and the purple-cyan-red light theme. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4b3d9dede8
commit
78ef438f9d
+33
@@ -0,0 +1,33 @@
|
|||||||
|
Create a website for kids and infants clothes store
|
||||||
|
nininaderi.com
|
||||||
|
|
||||||
|
Create sample content
|
||||||
|
Lang:fa
|
||||||
|
Directions: rtl
|
||||||
|
Use next js
|
||||||
|
Color palette is purple cyan red - light theme
|
||||||
|
|
||||||
|
Design only homepage
|
||||||
|
Use Persian font like iran-yekan or kalameh or iran sans and some cool kids like English font
|
||||||
|
Do not use images - show only placeholders with sizes
|
||||||
|
Do not use text over the images or banners - even slider
|
||||||
|
|
||||||
|
Sections are:
|
||||||
|
1.Top header bar
|
||||||
|
2. Header
|
||||||
|
3. Slider with two banner beside - height should be equal
|
||||||
|
4. Featured categories
|
||||||
|
5. Four features of our website :
|
||||||
|
ارسال رایگان بالاتر از ۵ میلیون تومان
|
||||||
|
ضمانت اصالت کالا
|
||||||
|
امکان مرجوعی کالا
|
||||||
|
رنگ بندی کامل
|
||||||
|
|
||||||
|
6. Product Special sale carousel
|
||||||
|
7. Two banner
|
||||||
|
8. Product Top selling carousel
|
||||||
|
9. 3 banner
|
||||||
|
10. Yeganeh brand product carousel
|
||||||
|
11. About nini Naderi
|
||||||
|
12. Latest blog carousel
|
||||||
|
13. footer
|
||||||
+127
-14
@@ -1,26 +1,139 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--purple-50: #f6f1ff;
|
||||||
--foreground: #171717;
|
--purple-100: #ede4ff;
|
||||||
|
--purple-200: #d8c6ff;
|
||||||
|
--purple-300: #c4a5ff;
|
||||||
|
--purple-500: #8b5cf6;
|
||||||
|
--purple-600: #7c3aed;
|
||||||
|
--purple-700: #6d28d9;
|
||||||
|
--purple-800: #5b21b6;
|
||||||
|
--purple-900: #3b0764;
|
||||||
|
--cyan-50: #ecfeff;
|
||||||
|
--cyan-100: #cffafe;
|
||||||
|
--cyan-200: #a5f3fc;
|
||||||
|
--cyan-300: #67e8f9;
|
||||||
|
--cyan-400: #22d3ee;
|
||||||
|
--cyan-500: #06b6d4;
|
||||||
|
--cyan-600: #0891b2;
|
||||||
|
--rose-50: #fff1f2;
|
||||||
|
--rose-100: #ffe4e6;
|
||||||
|
--rose-200: #fecdd3;
|
||||||
|
--rose-300: #fda4af;
|
||||||
|
--rose-500: #f43f5e;
|
||||||
|
--rose-600: #e11d48;
|
||||||
|
--ink: #2a1838;
|
||||||
|
--muted: #6d5b78;
|
||||||
|
--line: #efe6f5;
|
||||||
|
--paper: #fffafd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-purple-50: var(--purple-50);
|
||||||
--color-foreground: var(--foreground);
|
--color-purple-100: var(--purple-100);
|
||||||
--font-sans: var(--font-geist-sans);
|
--color-purple-200: var(--purple-200);
|
||||||
--font-mono: var(--font-geist-mono);
|
--color-purple-300: var(--purple-300);
|
||||||
|
--color-purple-500: var(--purple-500);
|
||||||
|
--color-purple-600: var(--purple-600);
|
||||||
|
--color-purple-700: var(--purple-700);
|
||||||
|
--color-purple-800: var(--purple-800);
|
||||||
|
--color-purple-900: var(--purple-900);
|
||||||
|
--color-cyan-50: var(--cyan-50);
|
||||||
|
--color-cyan-100: var(--cyan-100);
|
||||||
|
--color-cyan-200: var(--cyan-200);
|
||||||
|
--color-cyan-300: var(--cyan-300);
|
||||||
|
--color-cyan-400: var(--cyan-400);
|
||||||
|
--color-cyan-500: var(--cyan-500);
|
||||||
|
--color-cyan-600: var(--cyan-600);
|
||||||
|
--color-rose-50: var(--rose-50);
|
||||||
|
--color-rose-100: var(--rose-100);
|
||||||
|
--color-rose-200: var(--rose-200);
|
||||||
|
--color-rose-300: var(--rose-300);
|
||||||
|
--color-rose-500: var(--rose-500);
|
||||||
|
--color-rose-600: var(--rose-600);
|
||||||
|
--color-ink: var(--ink);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-line: var(--line);
|
||||||
|
--color-paper: var(--paper);
|
||||||
|
--font-sans: var(--font-vazirmatn), "IranYekan", "IRANSans", Tahoma, sans-serif;
|
||||||
|
--font-display: var(--font-fredoka), "Fredoka", "Comic Sans MS", cursive;
|
||||||
|
--shadow-card: 0 10px 30px -18px rgb(91 33 182 / 0.28);
|
||||||
|
--shadow-soft: 0 8px 24px -16px rgb(42 24 56 / 0.18);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
html {
|
||||||
:root {
|
scroll-behavior: smooth;
|
||||||
--background: #0a0a0a;
|
|
||||||
--foreground: #ededed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background-color: var(--paper);
|
||||||
color: var(--foreground);
|
background-image:
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
radial-gradient(ellipse 90% 42% at 10% 8%, rgb(237 228 255 / 0.9), transparent 70%),
|
||||||
|
radial-gradient(ellipse 80% 38% at 92% 6%, rgb(207 250 254 / 0.85), transparent 68%),
|
||||||
|
radial-gradient(ellipse 85% 40% at 78% 28%, rgb(255 228 230 / 0.8), transparent 70%),
|
||||||
|
radial-gradient(ellipse 70% 28% at 18% 36%, rgb(237 228 255 / 0.55), transparent 72%),
|
||||||
|
linear-gradient(to bottom, transparent 0%, transparent 62%, var(--paper) 100%);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 2200px;
|
||||||
|
background-position: top center;
|
||||||
|
color: var(--ink);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-box {
|
||||||
|
background: rgb(255 255 255 / 0.58);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
border: 1px solid rgb(255 255 255 / 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-display {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-count {
|
||||||
|
font-family: var(--font-count), var(--font-vazirmatn), sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-nn {
|
||||||
|
margin-inline: auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1280px;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container-nn {
|
||||||
|
padding-inline: 1.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-grid {
|
||||||
|
background-image: linear-gradient(135deg, rgb(255 255 255 / 0.22) 0%, transparent 48%);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar {
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-fade {
|
||||||
|
animation: slider-in 520ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slider-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(18px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-11
@@ -1,29 +1,40 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Baloo_Bhaijaan_2, Fredoka, Vazirmatn } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const vazirmatn = Vazirmatn({
|
||||||
variable: "--font-geist-sans",
|
subsets: ["arabic", "latin"],
|
||||||
subsets: ["latin"],
|
variable: "--font-vazirmatn",
|
||||||
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
const fredoka = Fredoka({
|
||||||
variable: "--font-geist-mono",
|
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
|
variable: "--font-fredoka",
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
|
const baloo = Baloo_Bhaijaan_2({
|
||||||
|
subsets: ["arabic", "latin"],
|
||||||
|
variable: "--font-count",
|
||||||
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "نینی نادری | فروشگاه پوشاک نوزاد و کودک",
|
||||||
description: "Generated by create next app",
|
description:
|
||||||
|
"فروشگاه نینی نادری؛ لباس نوزاد، دخترانه و پسرانه با رنگبندی کامل، ضمانت اصالت و ارسال رایگان بالای ۵ میلیون تومان.",
|
||||||
|
metadataBase: new URL("https://nininaderi.com"),
|
||||||
};
|
};
|
||||||
|
|
||||||
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} ${fredoka.variable} ${baloo.variable} h-full antialiased`}
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col">{children}</body>
|
<body className="min-h-full font-sans">{children}</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-60
@@ -1,69 +1,49 @@
|
|||||||
import Image from "next/image";
|
import { About } from "@/components/About";
|
||||||
|
import { DualBanners, TripleBanners } from "@/components/Banners";
|
||||||
|
import { BlogCarousel } from "@/components/BlogCarousel";
|
||||||
|
import { FeaturedCategories } from "@/components/FeaturedCategories";
|
||||||
|
import { Features } from "@/components/Features";
|
||||||
|
import { Footer } from "@/components/Footer";
|
||||||
|
import { Header } from "@/components/Header";
|
||||||
|
import { Hero } from "@/components/Hero";
|
||||||
|
import { ProductCarousel } from "@/components/ProductCarousel";
|
||||||
|
import { TopHeader } from "@/components/TopHeader";
|
||||||
|
import { saleProducts, topProducts, yeganehProducts } from "@/data/content";
|
||||||
|
|
||||||
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">
|
<div className="flex min-h-full flex-col">
|
||||||
<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">
|
<TopHeader />
|
||||||
<Image
|
<Header />
|
||||||
className="dark:invert h-5 w-[100px]"
|
<main className="flex-1">
|
||||||
src="/next.svg"
|
<Hero />
|
||||||
alt="Next.js logo"
|
<FeaturedCategories />
|
||||||
width={100}
|
<Features />
|
||||||
height={20}
|
<ProductCarousel
|
||||||
priority
|
id="sale"
|
||||||
|
english="Special Sale"
|
||||||
|
title="فروش ویژه نینیها"
|
||||||
|
products={saleProducts}
|
||||||
|
countdown
|
||||||
|
leadBanner
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
<DualBanners />
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
<ProductCarousel
|
||||||
To get started, edit the{" "}
|
english="Top Selling"
|
||||||
<code className="rounded bg-black/[.06] px-1.5 py-0.5 font-mono text-[0.9em] dark:bg-white/[.08]">
|
title="پرفروشترینها"
|
||||||
page.tsx
|
products={topProducts}
|
||||||
</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
|
<TripleBanners />
|
||||||
</a>
|
<ProductCarousel
|
||||||
<a
|
id="yeganeh"
|
||||||
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]"
|
english="Yeganeh"
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
title="محصولات برند یگانه"
|
||||||
target="_blank"
|
products={yeganehProducts}
|
||||||
rel="noopener noreferrer"
|
/>
|
||||||
>
|
<About />
|
||||||
Documentation
|
<BlogCarousel />
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { about } from "@/data/content";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
|
||||||
|
export function About() {
|
||||||
|
return (
|
||||||
|
<section id="about" className="container-nn py-10">
|
||||||
|
<div className="glass-box overflow-hidden rounded-[32px] shadow-soft">
|
||||||
|
<div className="grid lg:grid-cols-2">
|
||||||
|
<div className="p-6 sm:p-10">
|
||||||
|
<p className="font-display text-sm text-cyan-600">{about.english}</p>
|
||||||
|
<h2 className="mt-1 text-3xl font-black text-ink">{about.title}</h2>
|
||||||
|
<span className="mt-3 block h-1.5 w-16 rounded-full bg-linear-to-l from-purple-600 via-cyan-500 to-rose-500" />
|
||||||
|
<div className="mt-6 space-y-4 text-sm leading-8 text-muted">
|
||||||
|
{about.paragraphs.map((paragraph) => (
|
||||||
|
<p key={paragraph}>{paragraph}</p>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 grid grid-cols-3 gap-3">
|
||||||
|
{about.stats.map((stat) => (
|
||||||
|
<div key={stat.label} className="rounded-2xl bg-purple-50 px-3 py-4 text-center">
|
||||||
|
<p className="font-display text-lg text-purple-700">{stat.value}</p>
|
||||||
|
<p className="mt-1 text-xs text-muted">{stat.label}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bg-purple-50 p-4 sm:p-6">
|
||||||
|
<Placeholder
|
||||||
|
width={560}
|
||||||
|
height={400}
|
||||||
|
tone="purple"
|
||||||
|
rounded="rounded-[28px]"
|
||||||
|
className="h-full min-h-[280px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { dualBanners, tripleBanners } from "@/data/content";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
|
||||||
|
export function DualBanners() {
|
||||||
|
return (
|
||||||
|
<section className="container-nn py-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
{dualBanners.map((banner) => (
|
||||||
|
<a key={banner.id} href="#" className="overflow-hidden rounded-3xl">
|
||||||
|
<Placeholder width={640} height={280} tone={banner.tone} rounded="rounded-3xl" />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TripleBanners() {
|
||||||
|
return (
|
||||||
|
<section className="container-nn py-4">
|
||||||
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
|
{tripleBanners.map((banner) => (
|
||||||
|
<a key={banner.id} href="#" className="overflow-hidden rounded-3xl">
|
||||||
|
<Placeholder width={420} height={240} tone={banner.tone} rounded="rounded-3xl" />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { posts } from "@/data/content";
|
||||||
|
import { Carousel } from "./Carousel";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
import { SectionHeading } from "./SectionHeading";
|
||||||
|
|
||||||
|
export function BlogCarousel() {
|
||||||
|
return (
|
||||||
|
<section id="blog" className="container-nn py-8">
|
||||||
|
<SectionHeading english="Tiny Stories" title="تازههای مجله نینی" />
|
||||||
|
<Carousel>
|
||||||
|
{posts.map((post) => (
|
||||||
|
<article
|
||||||
|
key={post.id}
|
||||||
|
data-card
|
||||||
|
className="glass-box w-[280px] shrink-0 snap-start overflow-hidden rounded-3xl shadow-soft sm:w-[320px]"
|
||||||
|
>
|
||||||
|
<Placeholder width={360} height={220} tone={post.tone} rounded="rounded-none" />
|
||||||
|
<div className="p-4">
|
||||||
|
<p className="text-xs text-cyan-600">{post.date}</p>
|
||||||
|
<h3 className="mt-2 line-clamp-2 min-h-12 text-base font-bold leading-6 text-ink">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
<p className="mt-2 line-clamp-2 text-sm leading-7 text-muted">{post.excerpt}</p>
|
||||||
|
<a href="#" className="mt-3 inline-block text-sm font-bold text-purple-700">
|
||||||
|
ادامه مطلب
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRef, type ReactNode } from "react";
|
||||||
|
import { IconChevron } from "./Icons";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Carousel({ children }: Props) {
|
||||||
|
const scroller = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const move = (direction: 1 | -1) => {
|
||||||
|
const el = scroller.current;
|
||||||
|
if (!el) return;
|
||||||
|
const card = el.querySelector<HTMLElement>("[data-card]");
|
||||||
|
const amount = (card?.offsetWidth ?? 260) + 16;
|
||||||
|
const rtl = getComputedStyle(el).direction === "rtl";
|
||||||
|
el.scrollBy({ left: amount * direction * (rtl ? -1 : 1), behavior: "smooth" });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => move(-1)}
|
||||||
|
className="absolute top-1/2 right-0 z-10 hidden size-10 -translate-y-1/2 translate-x-1/2 items-center justify-center rounded-full border border-line bg-white text-purple-700 shadow-soft transition hover:bg-purple-50 md:flex"
|
||||||
|
aria-label="قبلی"
|
||||||
|
>
|
||||||
|
<IconChevron className="size-5 rotate-180" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => move(1)}
|
||||||
|
className="absolute top-1/2 left-0 z-10 hidden size-10 -translate-y-1/2 -translate-x-1/2 items-center justify-center rounded-full border border-line bg-white text-purple-700 shadow-soft transition hover:bg-purple-50 md:flex"
|
||||||
|
aria-label="بعدی"
|
||||||
|
>
|
||||||
|
<IconChevron className="size-5" />
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
ref={scroller}
|
||||||
|
className="hide-scrollbar flex gap-4 overflow-x-auto pb-2 snap-x snap-mandatory"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { categories } from "@/data/content";
|
||||||
|
import { toFaDigits } from "@/lib/format";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
import { SectionHeading } from "./SectionHeading";
|
||||||
|
|
||||||
|
export function FeaturedCategories() {
|
||||||
|
return (
|
||||||
|
<section id="categories" className="container-nn py-8">
|
||||||
|
<SectionHeading english="Little Wardrobe" title="دستهبندیهای محبوب" />
|
||||||
|
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-8">
|
||||||
|
{categories.map((category) => (
|
||||||
|
<a
|
||||||
|
key={category.id}
|
||||||
|
href="#"
|
||||||
|
className="glass-box group rounded-3xl p-3 text-center shadow-soft transition hover:-translate-y-1"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width={160}
|
||||||
|
height={160}
|
||||||
|
tone={category.tone}
|
||||||
|
rounded="rounded-2xl"
|
||||||
|
/>
|
||||||
|
<h3 className="mt-3 text-sm font-bold text-ink">{category.title}</h3>
|
||||||
|
<p className="text-xs text-muted">{toFaDigits(category.count)} مدل</p>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { features } from "@/data/content";
|
||||||
|
import { IconPalette, IconReturn, IconShield, IconTruck } from "./Icons";
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
truck: IconTruck,
|
||||||
|
shield: IconShield,
|
||||||
|
return: IconReturn,
|
||||||
|
palette: IconPalette,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Features() {
|
||||||
|
return (
|
||||||
|
<section className="container-nn py-4">
|
||||||
|
<div className="glass-box grid gap-3 rounded-[28px] p-3 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
|
{features.map((feature, index) => {
|
||||||
|
const Icon = icons[feature.icon];
|
||||||
|
const colors = [
|
||||||
|
"bg-purple-50 text-purple-700",
|
||||||
|
"bg-cyan-50 text-cyan-600",
|
||||||
|
"bg-rose-50 text-rose-600",
|
||||||
|
"bg-purple-50 text-cyan-600",
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
key={feature.id}
|
||||||
|
className="flex items-start gap-3 rounded-2xl px-3 py-4"
|
||||||
|
>
|
||||||
|
<span className={`grid size-12 shrink-0 place-items-center rounded-2xl ${colors[index]}`}>
|
||||||
|
<Icon />
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-bold leading-6 text-ink">{feature.title}</h3>
|
||||||
|
<p className="mt-1 text-xs leading-6 text-muted">{feature.text}</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { footer } from "@/data/content";
|
||||||
|
import { IconInstagram, IconPhone } from "./Icons";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
|
||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="mt-6 border-t border-white/60 bg-white/70 backdrop-blur-xl">
|
||||||
|
<div className="container-nn grid gap-8 py-12 sm:grid-cols-2 lg:grid-cols-6">
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<Link href="/" className="flex items-center gap-2.5">
|
||||||
|
<span className="grid size-11 place-items-center rounded-2xl bg-linear-to-br from-purple-600 via-cyan-500 to-rose-500 text-white">
|
||||||
|
<span className="font-display text-lg">NN</span>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<span className="font-display block text-purple-700">Nini Naderi</span>
|
||||||
|
<span className="block text-sm font-bold">نینی نادری</span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
<p className="mt-4 max-w-sm text-sm leading-7 text-muted">{footer.blurb}</p>
|
||||||
|
<div className="mt-4 flex items-center gap-3 text-sm text-ink">
|
||||||
|
<IconPhone className="size-4 text-purple-700" />
|
||||||
|
{footer.contact.phone}
|
||||||
|
</div>
|
||||||
|
<a href="#" className="mt-3 inline-flex items-center gap-2 text-sm text-purple-700">
|
||||||
|
<IconInstagram className="size-4" />
|
||||||
|
nininaderi
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FooterList title="خرید" items={footer.shop} />
|
||||||
|
<FooterList title="پشتیبانی" items={footer.help} />
|
||||||
|
<FooterList title="نینی نادری" items={footer.about} />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="mb-3 text-sm font-bold">تماس و اعتماد</h3>
|
||||||
|
<p className="text-sm leading-7 text-muted">{footer.contact.address}</p>
|
||||||
|
<p className="mt-2 text-sm text-ink">{footer.contact.email}</p>
|
||||||
|
<div className="mt-4 flex gap-2">
|
||||||
|
<div>
|
||||||
|
<Placeholder width={80} height={80} tone="purple" rounded="rounded-xl" />
|
||||||
|
<p className="mt-1 text-center text-[11px] text-muted">اینماد</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Placeholder width={80} height={80} tone="cyan" rounded="rounded-xl" />
|
||||||
|
<p className="mt-1 text-center text-[11px] text-muted">ساماندهی</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="border-t border-line">
|
||||||
|
<div className="container-nn flex flex-col items-center justify-between gap-2 py-4 text-xs text-muted sm:flex-row">
|
||||||
|
<p>© ۱۴۰۵ نینی نادری — nininaderi.com — همه حقوق محفوظ است.</p>
|
||||||
|
<p className="font-display text-purple-600">Made for little wardrobes</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FooterList({ title, items }: { title: string; items: string[] }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className="mb-3 text-sm font-bold">{title}</h3>
|
||||||
|
<ul className="space-y-2 text-sm text-muted">
|
||||||
|
{items.map((item) => (
|
||||||
|
<li key={item}>
|
||||||
|
<a href="#" className="hover:text-purple-700">
|
||||||
|
{item}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { navLinks } from "@/data/content";
|
||||||
|
import { IconBag, IconClose, IconHeart, IconMenu, IconSearch, IconUser } from "./Icons";
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="sticky top-0 z-40 border-b border-white/60 bg-white/55 backdrop-blur-xl">
|
||||||
|
<div className="container-nn">
|
||||||
|
<div className="flex h-[72px] items-center gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-ink lg:hidden"
|
||||||
|
onClick={() => setOpen((value) => !value)}
|
||||||
|
aria-label={open ? "بستن منو" : "باز کردن منو"}
|
||||||
|
>
|
||||||
|
{open ? <IconClose /> : <IconMenu />}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Link href="/" className="flex items-center gap-2.5">
|
||||||
|
<span className="grid size-11 place-items-center rounded-2xl bg-linear-to-br from-purple-600 via-cyan-500 to-rose-500 text-white shadow-card">
|
||||||
|
<span className="font-display text-lg leading-none">NN</span>
|
||||||
|
</span>
|
||||||
|
<span className="leading-tight">
|
||||||
|
<span className="font-display block text-[15px] text-purple-700">Nini Naderi</span>
|
||||||
|
<span className="block text-sm font-bold text-ink">نینی نادری</span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<form className="relative mx-auto hidden min-w-0 flex-1 max-w-xl md:block">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
placeholder="جستجوی لباس نوزاد، دخترانه، یگانه..."
|
||||||
|
className="h-11 w-full rounded-full border border-line bg-purple-50/60 pr-4 pl-11 text-sm outline-none transition focus:border-purple-500 focus:bg-white"
|
||||||
|
/>
|
||||||
|
<span className="pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 text-muted">
|
||||||
|
<IconSearch />
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="ms-auto flex items-center gap-1 sm:gap-2">
|
||||||
|
<a href="#" className="hidden items-center gap-1.5 rounded-full px-3 py-2 text-sm text-ink hover:bg-purple-50 lg:inline-flex">
|
||||||
|
<IconUser />
|
||||||
|
ورود
|
||||||
|
</a>
|
||||||
|
<a href="#" className="relative grid size-10 place-items-center rounded-full text-ink hover:bg-purple-50" aria-label="علاقهمندی">
|
||||||
|
<IconHeart />
|
||||||
|
<span className="absolute top-1 left-1 grid size-4 place-items-center rounded-full bg-rose-500 text-[10px] text-white">
|
||||||
|
۲
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a href="#" className="relative grid size-10 place-items-center rounded-full text-ink hover:bg-purple-50" aria-label="سبد خرید">
|
||||||
|
<IconBag />
|
||||||
|
<span className="absolute top-1 left-1 grid size-4 place-items-center rounded-full bg-cyan-500 text-[10px] text-white">
|
||||||
|
۳
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form className="pb-3 md:hidden">
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
placeholder="جستجو در نینی نادری"
|
||||||
|
className="h-10 w-full rounded-full border border-line bg-purple-50/60 pr-4 pl-10 text-sm outline-none"
|
||||||
|
/>
|
||||||
|
<span className="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-muted">
|
||||||
|
<IconSearch className="size-4" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<nav className="hidden h-12 items-center gap-6 text-sm font-medium text-ink lg:flex">
|
||||||
|
{navLinks.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.label}
|
||||||
|
href={link.href}
|
||||||
|
className="relative py-3 transition hover:text-purple-700 after:absolute after:right-0 after:bottom-1 after:h-0.5 after:w-0 after:bg-linear-to-l after:from-purple-600 after:to-cyan-500 after:transition-all hover:after:w-full"
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{open ? (
|
||||||
|
<div className="border-t border-line bg-white lg:hidden">
|
||||||
|
<nav className="container-nn flex flex-col py-2 text-sm">
|
||||||
|
{navLinks.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.label}
|
||||||
|
href={link.href}
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
className="border-b border-line/70 py-3 text-ink"
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { sideBanners, slides } from "@/data/content";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
|
||||||
|
export function Hero() {
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
const slide = slides[index];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = window.setInterval(() => {
|
||||||
|
setIndex((current) => (current + 1) % slides.length);
|
||||||
|
}, 5200);
|
||||||
|
return () => window.clearInterval(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="container-nn py-5 md:py-7">
|
||||||
|
<div className="grid gap-4 lg:grid-cols-12 lg:grid-rows-2">
|
||||||
|
<div className="relative overflow-hidden rounded-3xl lg:col-span-8 lg:row-span-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
key={slide.id}
|
||||||
|
className="slider-fade block h-full w-full"
|
||||||
|
onClick={() => setIndex((current) => (current + 1) % slides.length)}
|
||||||
|
aria-label="اسلاید بعد"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width={960}
|
||||||
|
height={420}
|
||||||
|
tone={slide.tone}
|
||||||
|
rounded="rounded-3xl"
|
||||||
|
fill
|
||||||
|
className="aspect-[960/420] h-full w-full lg:aspect-auto"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<div className="pointer-events-none absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2">
|
||||||
|
{slides.map((item, itemIndex) => (
|
||||||
|
<span
|
||||||
|
key={item.id}
|
||||||
|
className={`h-2 rounded-full ${
|
||||||
|
itemIndex === index ? "w-7 bg-white" : "w-2 bg-white/55"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{sideBanners.map((banner) => (
|
||||||
|
<a
|
||||||
|
key={banner.id}
|
||||||
|
href="#"
|
||||||
|
className="overflow-hidden rounded-3xl lg:col-span-4"
|
||||||
|
>
|
||||||
|
<Placeholder width={360} height={200} tone={banner.tone} rounded="rounded-3xl" />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
type IconProps = { className?: string };
|
||||||
|
|
||||||
|
export function IconSearch({ className = "size-5" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<circle cx="11" cy="11" r="7" stroke="currentColor" strokeWidth="1.8" />
|
||||||
|
<path d="M20 20l-3.2-3.2" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconUser({ className = "size-5" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<circle cx="12" cy="8" r="3.2" stroke="currentColor" strokeWidth="1.8" />
|
||||||
|
<path
|
||||||
|
d="M5 19.2c.8-3.2 3.4-5 7-5s6.2 1.8 7 5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconHeart({ className = "size-5" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M12 20s-7-4.4-7-9.2C5 8 6.8 6.4 9 6.4c1.3 0 2.3.6 3 1.6.7-1 1.7-1.6 3-1.6 2.2 0 4 1.6 4 4.4C19 15.6 12 20 12 20z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconBag({ className = "size-5" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M6.5 8h11l.8 11.2a1.5 1.5 0 0 1-1.5 1.6H7.2a1.5 1.5 0 0 1-1.5-1.6L6.5 8z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M9 8V7.2A3 3 0 0 1 12 4a3 3 0 0 1 3 3.2V8"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconPhone({ className = "size-4" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M7.2 3.8h2.4l1.2 3-1.8 1.2a12 12 0 0 0 6 6l1.2-1.8 3 1.2v2.4c0 .8-.6 1.5-1.4 1.6A15.6 15.6 0 0 1 5.6 5.2c.1-.8.8-1.4 1.6-1.4z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.6"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconTruck({ className = "size-7" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path d="M3 7h11v8H3z" stroke="currentColor" strokeWidth="1.7" />
|
||||||
|
<path d="M14 10h4l3 3v2h-7" stroke="currentColor" strokeWidth="1.7" />
|
||||||
|
<circle cx="7" cy="17.5" r="1.6" stroke="currentColor" strokeWidth="1.6" />
|
||||||
|
<circle cx="17" cy="17.5" r="1.6" stroke="currentColor" strokeWidth="1.6" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconShield({ className = "size-7" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M12 3.5 19 6v6.2c0 4.2-2.8 7.2-7 8.3-4.2-1.1-7-4.1-7-8.3V6l7-2.5z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
/>
|
||||||
|
<path d="m8.8 12 2.2 2.2 4.4-4.6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconReturn({ className = "size-7" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M7 8H4V5"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4.6 8A8 8 0 1 1 4 12.2"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconPalette({ className = "size-7" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path
|
||||||
|
d="M12 4a8 8 0 1 0 0 16h1.4a2.2 2.2 0 0 0 2.1-2.8 2 2 0 0 1 1.9-2.6H18a2 2 0 0 0 0-4h-.4A8 8 0 0 0 12 4z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.7"
|
||||||
|
/>
|
||||||
|
<circle cx="9" cy="10" r="1" fill="currentColor" />
|
||||||
|
<circle cx="12.5" cy="8.2" r="1" fill="currentColor" />
|
||||||
|
<circle cx="8.4" cy="13.4" r="1" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconChevron({ className = "size-5" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path d="m14.5 6-6 6 6 6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconMenu({ className = "size-6" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconClose({ className = "size-6" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<path d="M6 6l12 12M18 6 6 18" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IconInstagram({ className = "size-4" }: IconProps) {
|
||||||
|
return (
|
||||||
|
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden>
|
||||||
|
<rect x="4" y="4" width="16" height="16" rx="5" stroke="currentColor" strokeWidth="1.6" />
|
||||||
|
<circle cx="12" cy="12" r="3.4" stroke="currentColor" strokeWidth="1.6" />
|
||||||
|
<circle cx="17.2" cy="6.8" r="0.9" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import type { Tone } from "@/data/content";
|
||||||
|
|
||||||
|
const tones: Record<Tone, string> = {
|
||||||
|
purple: "bg-purple-200 from-purple-300 via-purple-200 to-purple-100 text-purple-800",
|
||||||
|
cyan: "bg-cyan-100 from-cyan-300 via-cyan-200 to-cyan-100 text-cyan-800",
|
||||||
|
rose: "bg-rose-100 from-rose-300 via-rose-200 to-rose-100 text-rose-700",
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
tone?: Tone;
|
||||||
|
className?: string;
|
||||||
|
rounded?: string;
|
||||||
|
fill?: boolean;
|
||||||
|
showSize?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Placeholder({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
tone = "purple",
|
||||||
|
className = "",
|
||||||
|
rounded = "rounded-2xl",
|
||||||
|
fill = false,
|
||||||
|
showSize = true,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`placeholder-grid relative flex flex-col items-center justify-center overflow-hidden bg-linear-to-br ${tones[tone]} ${rounded} ${className}`}
|
||||||
|
style={fill ? undefined : { aspectRatio: `${width} / ${height}` }}
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
{showSize ? (
|
||||||
|
<span className="font-display text-lg font-semibold tracking-wide sm:text-xl">
|
||||||
|
{width} × {height}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import type { Product } from "@/data/content";
|
||||||
|
import { formatPrice } from "@/lib/format";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
product: Product;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProductCard({ product }: Props) {
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
data-card
|
||||||
|
className="glass-box w-[220px] shrink-0 snap-start rounded-3xl p-2.5 shadow-soft transition hover:-translate-y-1 hover:shadow-card sm:w-[240px]"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width={280}
|
||||||
|
height={340}
|
||||||
|
tone={product.tone}
|
||||||
|
rounded="rounded-2xl"
|
||||||
|
/>
|
||||||
|
<div className="px-1.5 pt-3 pb-1">
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
<p className="font-display text-[11px] text-cyan-600">{product.brand}</p>
|
||||||
|
{product.badge ? (
|
||||||
|
<span
|
||||||
|
className={`rounded-full px-2 py-0.5 text-[10px] font-bold text-white ${
|
||||||
|
product.badge === "یگانه"
|
||||||
|
? "bg-purple-700"
|
||||||
|
: product.badge === "پرفروش"
|
||||||
|
? "bg-cyan-600"
|
||||||
|
: "bg-rose-500"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{product.badge}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<h3 className="mt-0.5 line-clamp-2 min-h-11 text-sm font-bold leading-6 text-ink">
|
||||||
|
{product.title}
|
||||||
|
</h3>
|
||||||
|
<div className="mt-2 flex items-center gap-1.5">
|
||||||
|
{product.colors.map((color) => (
|
||||||
|
<span
|
||||||
|
key={color}
|
||||||
|
className="size-3.5 rounded-full border border-white shadow-[0_0_0_1px_#e8dcef]"
|
||||||
|
style={{ backgroundColor: color }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex items-end justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
{product.oldPrice ? (
|
||||||
|
<p className="text-[11px] text-muted line-through">{formatPrice(product.oldPrice)}</p>
|
||||||
|
) : null}
|
||||||
|
<p className="text-sm font-black text-purple-700">{formatPrice(product.price)}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded-full bg-purple-700 px-3 py-1.5 text-xs font-bold text-white hover:bg-purple-800"
|
||||||
|
>
|
||||||
|
افزودن
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import type { Product } from "@/data/content";
|
||||||
|
import { toFaDigits } from "@/lib/format";
|
||||||
|
import { Carousel } from "./Carousel";
|
||||||
|
import { Placeholder } from "./Placeholder";
|
||||||
|
import { ProductCard } from "./ProductCard";
|
||||||
|
import { SectionHeading } from "./SectionHeading";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id?: string;
|
||||||
|
english: string;
|
||||||
|
title: string;
|
||||||
|
products: Product[];
|
||||||
|
countdown?: boolean;
|
||||||
|
leadBanner?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProductCarousel({
|
||||||
|
id,
|
||||||
|
english,
|
||||||
|
title,
|
||||||
|
products,
|
||||||
|
countdown,
|
||||||
|
leadBanner,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<section id={id} className="container-nn py-8">
|
||||||
|
<SectionHeading english={english} title={title}>
|
||||||
|
{countdown ? <SaleTimer /> : null}
|
||||||
|
</SectionHeading>
|
||||||
|
<Carousel>
|
||||||
|
{leadBanner ? (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
data-card
|
||||||
|
className="flex w-[220px] min-h-[280px] shrink-0 snap-start self-stretch overflow-hidden rounded-3xl sm:w-[240px]"
|
||||||
|
aria-label="بنر فروش ویژه"
|
||||||
|
>
|
||||||
|
<Placeholder
|
||||||
|
width={240}
|
||||||
|
height={400}
|
||||||
|
tone="rose"
|
||||||
|
rounded="rounded-3xl"
|
||||||
|
fill
|
||||||
|
showSize={false}
|
||||||
|
className="h-full w-full"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
{products.map((product) => (
|
||||||
|
<ProductCard key={product.id} product={product} />
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SaleTimer() {
|
||||||
|
const [left, setLeft] = useState({ h: 11, m: 42, s: 18 });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = window.setInterval(() => {
|
||||||
|
setLeft((current) => {
|
||||||
|
let { h, m, s } = current;
|
||||||
|
if (s > 0) s -= 1;
|
||||||
|
else if (m > 0) {
|
||||||
|
s = 59;
|
||||||
|
m -= 1;
|
||||||
|
} else if (h > 0) {
|
||||||
|
s = 59;
|
||||||
|
m = 59;
|
||||||
|
h -= 1;
|
||||||
|
} else {
|
||||||
|
h = 11;
|
||||||
|
m = 59;
|
||||||
|
s = 59;
|
||||||
|
}
|
||||||
|
return { h, m, s };
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
return () => window.clearInterval(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const cells = [
|
||||||
|
{ label: "ساعت", value: left.h },
|
||||||
|
{ label: "دقیقه", value: left.m },
|
||||||
|
{ label: "ثانیه", value: left.s },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-end gap-1.5">
|
||||||
|
{cells.map((cell, index) => (
|
||||||
|
<div key={cell.label} className="flex items-end gap-1.5">
|
||||||
|
{index > 0 ? (
|
||||||
|
<span className="font-count mb-5 text-xl leading-none text-rose-400">:</span>
|
||||||
|
) : null}
|
||||||
|
<div className="min-w-[3.25rem] rounded-2xl bg-linear-to-b from-rose-100 to-rose-50 px-2.5 py-2 text-center shadow-[inset_0_1px_0_rgb(255_255_255_/_0.8)]">
|
||||||
|
<p className="font-count text-[1.65rem] leading-none text-rose-600">
|
||||||
|
{toFaDigits(String(cell.value).padStart(2, "0"))}
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-[10px] font-medium text-rose-400">{cell.label}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
english: string;
|
||||||
|
title: string;
|
||||||
|
href?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SectionHeading({ english, title, href = "#", children }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="mb-6 flex flex-wrap items-end justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<p className="font-display text-sm tracking-wide text-cyan-600">{english}</p>
|
||||||
|
<h2 className="mt-1 text-2xl font-bold text-ink sm:text-3xl">{title}</h2>
|
||||||
|
<span className="mt-2 block h-1.5 w-16 rounded-full bg-linear-to-l from-purple-600 via-cyan-500 to-rose-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
{children}
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
className="hidden text-sm font-medium text-purple-700 transition hover:text-rose-600 sm:inline-flex"
|
||||||
|
>
|
||||||
|
مشاهده همه
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { IconInstagram, IconPhone } from "./Icons";
|
||||||
|
|
||||||
|
export function TopHeader() {
|
||||||
|
return (
|
||||||
|
<div className="bg-linear-to-l from-purple-800 via-purple-700 to-cyan-600 text-white">
|
||||||
|
<div className="container-nn flex h-10 items-center justify-between gap-3 text-xs sm:text-sm">
|
||||||
|
<p className="truncate">ارسال رایگان برای خریدهای بالای ۵ میلیون تومان</p>
|
||||||
|
<div className="flex shrink-0 items-center gap-4">
|
||||||
|
<a href="tel:02191001234" className="hidden items-center gap-1.5 sm:inline-flex">
|
||||||
|
<IconPhone />
|
||||||
|
<span>۰۲۱-۹۱۰۰۱۲۳۴</span>
|
||||||
|
</a>
|
||||||
|
<a href="#" className="inline-flex items-center gap-1.5" aria-label="اینستاگرام نینی نادری">
|
||||||
|
<IconInstagram />
|
||||||
|
<span className="hidden md:inline">nininaderi</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,430 @@
|
|||||||
|
export type Tone = "purple" | "cyan" | "rose";
|
||||||
|
|
||||||
|
export type Product = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
brand: string;
|
||||||
|
price: number;
|
||||||
|
oldPrice?: number;
|
||||||
|
colors: string[];
|
||||||
|
tone: Tone;
|
||||||
|
badge?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const navLinks = [
|
||||||
|
{ href: "#", label: "صفحه اصلی" },
|
||||||
|
{ href: "#categories", label: "نوزاد" },
|
||||||
|
{ href: "#categories", label: "دخترانه" },
|
||||||
|
{ href: "#categories", label: "پسرانه" },
|
||||||
|
{ href: "#yeganeh", label: "برند یگانه" },
|
||||||
|
{ href: "#sale", label: "فروش ویژه" },
|
||||||
|
{ href: "#blog", label: "وبلاگ" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const slides = [
|
||||||
|
{
|
||||||
|
id: "s1",
|
||||||
|
tone: "purple" as Tone,
|
||||||
|
kicker: "Summer 1405",
|
||||||
|
title: "کالکشن تابستان نینیها",
|
||||||
|
text: "لباسهای نخی سبک برای بازی، خواب و گردشهای گرم. رنگبندی کامل از یاسی تا فیروزهای.",
|
||||||
|
cta: "مشاهده کالکشن",
|
||||||
|
size: "960 × 420",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "s2",
|
||||||
|
tone: "rose" as Tone,
|
||||||
|
kicker: "Newborn Sale",
|
||||||
|
title: "تخفیف ویژه نوزادی تا ۴۰٪",
|
||||||
|
text: "بادی، سرهمی و ست بیمارستانی برای روزهای اول. نرم روی پوست و آماده هدیه.",
|
||||||
|
cta: "خرید نوزادی",
|
||||||
|
size: "960 × 420",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "s3",
|
||||||
|
tone: "cyan" as Tone,
|
||||||
|
kicker: "Yeganeh",
|
||||||
|
title: "برند یگانه حالا در نینی نادری",
|
||||||
|
text: "دوخت تمیز، پارچه لطیف و ستهای خواهر و برادری که با هم جور میشوند.",
|
||||||
|
cta: "محصولات یگانه",
|
||||||
|
size: "960 × 420",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const sideBanners = [
|
||||||
|
{
|
||||||
|
id: "sb1",
|
||||||
|
tone: "cyan" as Tone,
|
||||||
|
title: "ست خواهر و برادری",
|
||||||
|
text: "جفتهای رنگی برای عکسهای خانوادگی",
|
||||||
|
size: "360 × 200",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sb2",
|
||||||
|
tone: "rose" as Tone,
|
||||||
|
title: "کفش اولین قدم",
|
||||||
|
text: "پاپوش نرم برای نوپاهای کنجکاو",
|
||||||
|
size: "360 × 200",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const categories = [
|
||||||
|
{ id: "c1", title: "لباس نوزاد", count: 86, tone: "purple" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c2", title: "دخترانه", count: 124, tone: "rose" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c3", title: "پسرانه", count: 118, tone: "cyan" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c4", title: "ست و راحتی", count: 64, tone: "purple" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c5", title: "کفش و پاپوش", count: 41, tone: "cyan" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c6", title: "اکسسوری", count: 37, tone: "rose" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c7", title: "حمام و حوله", count: 22, tone: "purple" as Tone, size: "160 × 160" },
|
||||||
|
{ id: "c8", title: "مهمانی", count: 29, tone: "rose" as Tone, size: "160 × 160" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const features = [
|
||||||
|
{
|
||||||
|
id: "f1",
|
||||||
|
title: "ارسال رایگان بالاتر از ۵ میلیون تومان",
|
||||||
|
text: "سفارشهای بالای پنج میلیون، رایگان تا در خانه.",
|
||||||
|
icon: "truck" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "f2",
|
||||||
|
title: "ضمانت اصالت کالا",
|
||||||
|
text: "هر تگ و دوخت را قبل از ارسال بررسی میکنیم.",
|
||||||
|
icon: "shield" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "f3",
|
||||||
|
title: "امکان مرجوعی کالا",
|
||||||
|
text: "تا ۷ روز اگر سایز یا رنگ جور نبود برمیگردد.",
|
||||||
|
icon: "return" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "f4",
|
||||||
|
title: "رنگ بندی کامل",
|
||||||
|
text: "از یاسی و سرخابی تا فیروزه؛ برای هر سلیقهای.",
|
||||||
|
icon: "palette" as const,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const saleProducts: Product[] = [
|
||||||
|
{
|
||||||
|
id: "p1",
|
||||||
|
title: "سرهمی نخی نوزاد یاسی",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 890000,
|
||||||
|
oldPrice: 1290000,
|
||||||
|
colors: ["#C4B5FD", "#F9A8D4", "#67E8F9"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "۳۱٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p2",
|
||||||
|
title: "پیراهن چیندار دخترانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1450000,
|
||||||
|
oldPrice: 1890000,
|
||||||
|
colors: ["#FB7185", "#DDD6FE", "#FDE68A"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "۲۳٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p3",
|
||||||
|
title: "تیشرت طرح دایناسور",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 620000,
|
||||||
|
oldPrice: 840000,
|
||||||
|
colors: ["#22D3EE", "#34D399", "#F97316"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "۲۶٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p4",
|
||||||
|
title: "بادی آستین بلند ارگانیک",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 540000,
|
||||||
|
oldPrice: 780000,
|
||||||
|
colors: ["#E9D5FF", "#FECDD3", "#A5F3FC"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "۳۱٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p5",
|
||||||
|
title: "شلوار اسلش راحتی",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 710000,
|
||||||
|
oldPrice: 980000,
|
||||||
|
colors: ["#5B21B6", "#164E63", "#9F1239"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "۲۸٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p6",
|
||||||
|
title: "کت بافت پاییزه خردلی",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1680000,
|
||||||
|
oldPrice: 2190000,
|
||||||
|
colors: ["#FBBF24", "#C4B5FD", "#FDA4AF"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "۲۳٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p7",
|
||||||
|
title: "ست بیمارستانی نوزاد",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 1120000,
|
||||||
|
oldPrice: 1590000,
|
||||||
|
colors: ["#F5D0FE", "#BAE6FD", "#FEF3C7"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "۳۰٪",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p8",
|
||||||
|
title: "کفش پارچهای نوپا",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 980000,
|
||||||
|
oldPrice: 1350000,
|
||||||
|
colors: ["#F43F5E", "#06B6D4", "#7C3AED"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "۲۷٪",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const dualBanners = [
|
||||||
|
{
|
||||||
|
id: "db1",
|
||||||
|
tone: "purple" as Tone,
|
||||||
|
kicker: "Party Edit",
|
||||||
|
title: "کالکشن مهمانی کوچولوها",
|
||||||
|
text: "پیراهن، سارافون و کتهای نرم برای جشن تولد",
|
||||||
|
size: "640 × 280",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "db2",
|
||||||
|
tone: "cyan" as Tone,
|
||||||
|
kicker: "Bedtime",
|
||||||
|
title: "لباس خواب نخی",
|
||||||
|
text: "شبهای آرام با پارچه سبک و دوخت بدون خارش",
|
||||||
|
size: "640 × 280",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const topProducts: Product[] = [
|
||||||
|
{
|
||||||
|
id: "t1",
|
||||||
|
title: "ست خواهر برادری راه راه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1980000,
|
||||||
|
colors: ["#7C3AED", "#06B6D4", "#F43F5E"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t2",
|
||||||
|
title: "سارافون جین دخترانه",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 1320000,
|
||||||
|
colors: ["#93C5FD", "#F9A8D4", "#FDE68A"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t3",
|
||||||
|
title: "هودی گوشدار خرسی",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1540000,
|
||||||
|
colors: ["#FDBA74", "#C4B5FD", "#67E8F9"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t4",
|
||||||
|
title: "بادی دکمه فشاری سهتایی",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 870000,
|
||||||
|
colors: ["#FDE68A", "#DDD6FE", "#A7F3D0"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t5",
|
||||||
|
title: "شلوارک کتان پسرانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 760000,
|
||||||
|
colors: ["#155E75", "#44403C", "#7C3AED"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t6",
|
||||||
|
title: "کلاه و دستکش ست",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 390000,
|
||||||
|
colors: ["#FBCFE8", "#C4B5FD", "#A5F3FC"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t7",
|
||||||
|
title: "پیراهن لینن تابستانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1410000,
|
||||||
|
colors: ["#FDE68A", "#FECDD3", "#C7D2FE"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "t8",
|
||||||
|
title: "جوراب ساقبلند رنگی",
|
||||||
|
brand: "نینی نادری",
|
||||||
|
price: 210000,
|
||||||
|
colors: ["#F43F5E", "#06B6D4", "#8B5CF6"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "پرفروش",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const tripleBanners = [
|
||||||
|
{
|
||||||
|
id: "tb1",
|
||||||
|
tone: "rose" as Tone,
|
||||||
|
title: "دخترانه بهاری",
|
||||||
|
text: "گلگلی، چیندار و روشن",
|
||||||
|
size: "420 × 240",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tb2",
|
||||||
|
tone: "cyan" as Tone,
|
||||||
|
title: "پسرانه اسپرت",
|
||||||
|
text: "هودی، تیشرت و شلوارک بازی",
|
||||||
|
size: "420 × 240",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tb3",
|
||||||
|
tone: "purple" as Tone,
|
||||||
|
title: "نوزادی ارگانیک",
|
||||||
|
text: "پنبه لطیف برای پوست حساس",
|
||||||
|
size: "420 × 240",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const yeganehProducts: Product[] = [
|
||||||
|
{
|
||||||
|
id: "y1",
|
||||||
|
title: "سرهمی یگانه طرح ستاره",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1180000,
|
||||||
|
colors: ["#7C3AED", "#F472B6", "#22D3EE"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "y2",
|
||||||
|
title: "ست دوتکه یگانه دخترانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1720000,
|
||||||
|
colors: ["#FB7185", "#C4B5FD", "#FDE68A"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "y3",
|
||||||
|
title: "پولوشرت یگانه پسرانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 990000,
|
||||||
|
colors: ["#0891B2", "#1E3A5F", "#F43F5E"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "y4",
|
||||||
|
title: "بادی یگانه بسته دوتایی",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 860000,
|
||||||
|
colors: ["#E9D5FF", "#A5F3FC", "#FECDD3"],
|
||||||
|
tone: "purple",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "y5",
|
||||||
|
title: "پیراهن یقه ب ب یگانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1390000,
|
||||||
|
colors: ["#F9A8D4", "#FDE68A", "#C4B5FD"],
|
||||||
|
tone: "rose",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "y6",
|
||||||
|
title: "شلوار جین نرم یگانه",
|
||||||
|
brand: "یگانه",
|
||||||
|
price: 1250000,
|
||||||
|
colors: ["#93C5FD", "#64748B", "#7C3AED"],
|
||||||
|
tone: "cyan",
|
||||||
|
badge: "یگانه",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const about = {
|
||||||
|
title: "درباره نینی نادری",
|
||||||
|
english: "About Nini Naderi",
|
||||||
|
paragraphs: [
|
||||||
|
"نینی نادری از یک دغدغه ساده شروع شد: پیدا کردن لباسی که هم روی پوست نوزاد نرم باشد، هم رنگش زنده بماند و هم بعد از چند بار شستوشو از ریخت نیفتد. ما فروشگاه تخصصی پوشاک نوزاد و کودک هستیم؛ از بادی روزهای اول تا کفش اولین قدم.",
|
||||||
|
"برند یگانه یکی از ثابتهای قفسه ماست؛ بهخاطر دوخت تمیز و رنگبندی کاملی که برای خواهر و برادرها جور درمیآید. کنار آن، انتخابهای خود نینی نادری را با پارچههای نخی و سایزبندی دقیق کنار هم چیدهایم.",
|
||||||
|
"هر سفارش با ضمانت اصالت ارسال میشود، امکان مرجوعی دارید و برای خریدهای بالای ۵ میلیون تومان پیک رایگان است. اگر سایز شک دارید، پشتیبانی نینی نادری کمکتان میکند.",
|
||||||
|
],
|
||||||
|
stats: [
|
||||||
|
{ value: "۱۲ سال", label: "همراه خانوادهها" },
|
||||||
|
{ value: "+۴۰۰", label: "مدل فعال" },
|
||||||
|
{ value: "۲۴ ساعته", label: "پاسخگویی سفارش" },
|
||||||
|
],
|
||||||
|
imageSize: "560 × 400",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const posts = [
|
||||||
|
{
|
||||||
|
id: "b1",
|
||||||
|
tone: "purple" as Tone,
|
||||||
|
date: "۲۳ مرداد ۱۴۰۵",
|
||||||
|
title: "چطور سایز لباس نوزاد را درست حدس بزنیم؟",
|
||||||
|
excerpt: "جدول دور سینه، قد و وزن؛ بدون خرید اشتباه و مرجوعی اضافه.",
|
||||||
|
size: "360 × 220",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "b2",
|
||||||
|
tone: "cyan" as Tone,
|
||||||
|
date: "۱۸ مرداد ۱۴۰۵",
|
||||||
|
title: "پارچه نخی بهتر است یا پنبه ارگانیک؟",
|
||||||
|
excerpt: "برای پوست حساس تابستان، کدام بافت خنکتر و کمحساسیتتر است.",
|
||||||
|
size: "360 × 220",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "b3",
|
||||||
|
tone: "rose" as Tone,
|
||||||
|
date: "۹ مرداد ۱۴۰۵",
|
||||||
|
title: "کفش اولین قدم؛ نرم باشد یا محکم؟",
|
||||||
|
excerpt: "پاپوش مناسب نوپا باید قوزک را نگه دارد اما انگشت را آزاد بگذارد.",
|
||||||
|
size: "360 × 220",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "b4",
|
||||||
|
tone: "purple" as Tone,
|
||||||
|
date: "۱ مرداد ۱۴۰۵",
|
||||||
|
title: "ست خواهر و برادری برای عکس تولد",
|
||||||
|
excerpt: "چطور رنگها را با هم هماهنگ کنیم بدون اینکه لباسها شبیه یونیفرم شوند.",
|
||||||
|
size: "360 × 220",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const footer = {
|
||||||
|
blurb:
|
||||||
|
"فروشگاه پوشاک نوزاد و کودک نینی نادری. لباس نرم، رنگ زنده، ارسال سریع.",
|
||||||
|
shop: ["نوزاد", "دخترانه", "پسرانه", "برند یگانه", "فروش ویژه"],
|
||||||
|
help: ["پیگیری سفارش", "راهنمای سایز", "مرجوعی کالا", "سوالات پرتکرار", "تماس با ما"],
|
||||||
|
about: ["داستان نینی نادری", "شعب حضوری", "فرصت همکاری", "قوانین و حریم خصوصی"],
|
||||||
|
contact: {
|
||||||
|
phone: "۰۲۱-۹۱۰۰۱۲۳۴",
|
||||||
|
mobile: "۰۹۱۲۳۴۵۶۷۸۹",
|
||||||
|
email: "hello@nininaderi.com",
|
||||||
|
address: "تهران، خیابان شریعتی، بالاتر از میرداماد، پلاک ۲۴۸",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const FA_DIGITS = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
|
||||||
|
|
||||||
|
export function toFaDigits(value: number | string) {
|
||||||
|
return String(value).replace(/\d/g, (digit) => FA_DIGITS[Number(digit)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatPrice(value: number) {
|
||||||
|
return `${toFaDigits(value.toLocaleString("en-US"))} تومان`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user