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:
Alireza Hassani
2026-08-13 14:04:45 +03:30
co-authored by Cursor
parent 4b3d9dede8
commit 78ef438f9d
21 changed files with 1559 additions and 86 deletions
+33
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>
); );
} }
+40
View File
@@ -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>
);
}
+30
View File
@@ -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>
);
}
+33
View File
@@ -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>
);
}
+48
View File
@@ -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>
);
}
+30
View File
@@ -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>
);
}
+41
View File
@@ -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>
);
}
+76
View File
@@ -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>
);
}
+109
View File
@@ -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>
);
}
+62
View File
@@ -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>
);
}
+161
View File
@@ -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>
);
}
+41
View File
@@ -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>
);
}
+67
View File
@@ -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>
);
}
+109
View File
@@ -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>
);
}
+29
View File
@@ -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>
);
}
+21
View File
@@ -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>
);
}
+430
View File
@@ -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: "تهران، خیابان شریعتی، بالاتر از میرداماد، پلاک ۲۴۸",
},
};
+9
View File
@@ -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"))} تومان`;
}