Initial commit: Ali Alemi storefront (Next.js, Meshkee API integration)

Farsi RTL Next.js 16 storefront for استاد علی عالمی, cloned from the
original Meshkee web-builder site and rebuilt with Tailwind 4, live
Meshkee API integration (products, store items, portfolios, blog,
workshops, comments, cart, customer auth), and standalone production
output for the shared-VM deploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
amirhosein.ashourloo
2026-09-16 09:43:10 +03:30
co-authored by Claude Sonnet 5
commit 8f102a8250
106 changed files with 11605 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
"use client";
import { useRef } from "react";
import type { Swiper as SwiperClass } from "swiper";
import { Autoplay, Navigation } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import { IconChevron } from "@/components/icons";
import { Reveal } from "@/components/Reveal";
import { FEATURES } from "./features-data";
// Cards + a right-hand heading/nav column, matching the original theme's
// feature section exactly (Owl Carousel there, Swiper here per instruction).
export function FeatureCards() {
const swiperRef = useRef<SwiperClass | null>(null);
return (
<section className="py-16 md:py-24">
<div className="boxed grid gap-10 md:grid-cols-[280px_minmax(0,1fr)]">
<Reveal className="text-center md:text-right">
<h2 className="text-3xl font-bold text-ink">ویژگی‌های ما</h2>
<p className="mt-4 text-sm leading-7 text-body">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.
</p>
<div className="mt-8 flex items-center justify-center gap-3 md:justify-start">
<button
type="button"
aria-label="اسلاید بعدی"
onClick={() => swiperRef.current?.slideNext()}
className="flex h-10 w-10 items-center justify-center rounded-full border border-line text-ink-soft transition-colors hover:border-primary hover:text-primary"
>
<IconChevron className="h-4 w-4 -scale-x-100" />
</button>
<button
type="button"
aria-label="اسلاید قبلی"
onClick={() => swiperRef.current?.slidePrev()}
className="flex h-10 w-10 items-center justify-center rounded-full border border-line text-ink-soft transition-colors hover:border-primary hover:text-primary"
>
<IconChevron className="h-4 w-4" />
</button>
</div>
</Reveal>
<Reveal delay={100} className="min-w-0">
<Swiper
modules={[Autoplay, Navigation]}
slidesPerView={1}
spaceBetween={24}
autoplay={{ delay: 4500, disableOnInteraction: false }}
onSwiper={(instance) => {
swiperRef.current = instance;
}}
breakpoints={{ 640: { slidesPerView: 2 }, 1024: { slidesPerView: 3 } }}
>
{FEATURES.map(({ icon: Icon, title, text, tone }) => (
<SwiperSlide key={title}>
<div className="flex h-full flex-col rounded-2xl bg-white p-7 shadow-sm ring-1 ring-line">
<span
className={`flex h-16 w-16 items-center justify-center rounded-2xl text-white ${
tone === "primary" ? "bg-primary" : "bg-secondary"
}`}
>
<Icon className="h-7 w-7" />
</span>
<h3 className="mt-6 text-lg font-bold text-ink">{title}</h3>
<p className="mt-3 text-sm leading-6 text-body">{text}</p>
</div>
</SwiperSlide>
))}
</Swiper>
</Reveal>
</div>
</section>
);
}