Initial import of 1407 block sources under blocks/

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ar.badiei
2026-07-11 21:21:29 +03:30
co-authored by Cursor
commit 85836ef183
1412 changed files with 303567 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
.DS_Store
Thumbs.db
+28
View File
@@ -0,0 +1,28 @@
# reui-blocks
Block source files for the Shadsite catalog.
## Layout
```
blocks/
{slug}.tsx # flat block sources (1407 files)
test/ # auxiliary imports (e.g. BlogBlocksPage)
```
Consumed by the main app via `BLOCKS_ROOT=./storage/blocks`.
## Clone with main repo
```bash
git submodule update --init --recursive
```
## Update blocks in main repo
```bash
cd storage
git pull origin main
cd ..
npm run blocks:sync-registry
```
View File
+109
View File
@@ -0,0 +1,109 @@
import { BlogBlocksCard } from "./test/BlogBlocksCard";
import { BlogBlocksCategoryTabs } from "./test/BlogBlocksCategoryTabs";
const categories = [
"همه",
"شرکت",
"بازاریابی",
"اتاق خبر",
"شرکا",
"مهندسی",
"مطبوعات",
];
const cards = [
{
category: "شرکت",
title: "پذیرش فرهنگ کار از راه دور",
description:
"مزایا و چالش‌های پذیرش فرهنگ دورکاری در کسب‌وکارهای امروزی را بررسی کنید.",
date: "۱۸ آگوست ۲۲۵",
authors: ["Meschac Irung"],
},
{
category: "بازاریابی",
title: "آینده تحول‌آفرین هوش مصنوعی در بازاریابی دیجیتال",
description:
"بررسی کنید هوش مصنوعی چگونه استراتژی‌های بازاریابی و تعامل با مشتری را متحول می‌کند.",
date: "۱۰ سپتامبر ۲۲۵",
authors: ["Theo Balick", "Meschac Irung"],
},
{
category: "اتاق خبر",
title: "نوآوری‌های پیشرفته در تحلیل داده",
description:
"با جدیدترین روندهای تحلیل داده آشنا شوید و ببینید چگونه رشد کسب‌وکار را هدایت می‌کنند.",
date: "۵ اکتبر ۲۲۵",
authors: ["Shadcn", "Meschac Irung"],
},
{
category: "مهندسی",
title: "پیشرفت‌ها در امنیت سایبری",
description:
"فناوری‌های تازه‌ای را بشناسید که کسب‌وکارها را در برابر تهدیدهای دیجیتال محافظت می‌کنند.",
date: "۱۵ اکتبر ۲۲۵",
authors: ["Meschac Irung"],
},
{
category: "مطبوعات",
title: "نقش بلاک‌چین در مالی مدرن",
description: "بررسی کنید بلاک‌چین چگونه آینده خدمات مالی را متحول می‌کند.",
date: "۱ نوامبر ۲۲۵",
authors: ["Theo Balick"],
},
{
category: "شرکت",
title: "پایداری در فناوری: تمرکز رو به رشد",
description: "با رویکردها و راهکارهای توسعه پایدار در صنعت فناوری آشنا شوید.",
date: "۱۰ نوامبر ۲۲۵",
authors: ["Shadcn"],
},
{
category: "بازاریابی",
title: "بهره‌گیری از کلان‌داده برای موفقیت کسب‌وکار",
description:
"بررسی کنید تحلیل کلان‌داده چگونه فرصت‌های جدیدی برای رشد کسب‌وکار ایجاد می‌کند.",
date: "۵ دسامبر ۲۲۵",
authors: ["Bernard Ngandu", "Glodie Lukose"],
},
];
export function BlogBlocksPage() {
return (
<main role="main">
<div className="bg-background scheme-light selection:bg-foreground/10 selection:text-foreground dark:scheme-dark">
<section className="@container bg-background py-16 md:py-24">
<div className="mx-auto max-w-5xl px-6">
<div className="max-w-md">
<span className="text-muted-foreground">وبلاگ</span>
<h1 className="mt-4 text-xl font-semibold text-balance text-muted-foreground">
اخبار، تحلیل‌ها و مطالب بیشتر از{" "}
<strong className="font-semibold text-foreground">تیلارک کوارتز</strong>
</h1>
</div>
</div>
<div className="sticky top-0 z-10 my-12 bg-background">
<div className="mx-auto max-w-5xl px-6">
<div className="flex justify-between gap-4 border-b max-md:-mx-6">
<BlogBlocksCategoryTabs labels={categories} />
</div>
</div>
</div>
<div className="mx-auto max-w-5xl px-6">
<div className="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3 auto-rows-fr">
{cards.map((card) => (
<BlogBlocksCard
key={`${card.title}-${card.date}`}
category={card.category}
title={card.title}
description={card.description}
date={card.date}
authors={card.authors}
/>
))}
</div>
</div>
</section>
</div>
</main>
);
}
+84
View File
@@ -0,0 +1,84 @@
import { CircleArrowRight, Files, Settings } from "lucide-react";
import { cn } from "@/lib/utils";
interface About1Props {
className?: string;
}
const About1 = ({ className }: About1Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container flex flex-col gap-16 lg:gap-28">
<div className="flex flex-col gap-4 lg:gap-8">
<h1 className="text-xl font-semibold tracking-tighter lg:text-2xll">
درباره ما
</h1>
<p className="max-w-xl text-xl">
پلتفرم ما ساخت پورتال مشتری، سامانه‌های سامانه ارتباط با مشتری و فروش، ابزارهای داخلی و نرم‌افزارهای سازمانی را برای تیم شما ساده می‌کند؛ در چند دقیقه، نه چند ماه.
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="placeholder"
className="size-full max-h-96 rounded-2xl object-cover"
/>
<div className="flex flex-col justify-between gap-10 rounded-2xl bg-muted bg-[url('/block-media/shadcnblocks/block/photos/christin-hume-Hcfwew744z4-unsplash.jpg')] bg-cover bg-center p-10">
<p className="text-sm font-semibold text-white">مأموریت ما</p>
<p className="text-lg font-medium text-white">
باور ما این است که ساخت نرم‌افزار باید برای همه ساده و در دسترس باشد. هر کسی باید بتواند بدون نیاز به طراح یا برنامه‌نویس، ابزار موردنیاز خود را بسازد.
</p>
</div>
</div>
<div className="flex flex-col gap-6 md:gap-20">
<div className="max-w-xl">
<h2 className="mb-4 text-3xl font-semibold tracking-tight md:text-2xll">
ساخت نرم‌افزار را ساده کرده‌ایم
</h2>
<p className="text-lg text-muted-foreground">
ما قصد داریم به یک میلیون تیم کمک کنیم تا نرم‌افزار اختصاصی خود را بسازند. این هم برنامه‌ی ما برای رسیدن به این هدف است.
</p>
</div>
<div className="grid gap-10 md:grid-cols-3">
<div className="flex flex-col">
<div className="mb-5 flex size-12 items-center justify-center rounded-2xl bg-accent">
<Files className="size-5" />
</div>
<h3 className="mt-2 mb-3 text-lg font-semibold">
شفافیت و همکاری
</h3>
<p className="text-muted-foreground">
ما باور داریم همکاری زمانی شکل می‌گیرد که فروتنی، احترام و روحیه کمک به یکدیگر بخشی از فرهنگ تیم باشد. بازخورد سازنده، گفت‌وگوی شفاف و یادگیری مداوم از ارزش‌های اصلی ما هستند.
</p>
</div>
<div className="flex flex-col">
<div className="mb-5 flex size-12 items-center justify-center rounded-2xl bg-accent">
<CircleArrowRight className="size-5" />
</div>
<h3 className="mt-2 mb-3 text-lg font-semibold">
ایجاد تغییر چشمگیر
</h3>
<p className="text-muted-foreground">
با جسارت، شجاعت و اهداف شفاف. ما به دنبال فرصت‌های بزرگ هستیم و تمرکز و انرژی خود را بر مهم‌ترین کارهایی که باید انجام شوند، دوچندان می‌کنیم.
</p>
</div>
<div className="flex flex-col">
<div className="mb-5 flex size-12 items-center justify-center rounded-2xl bg-accent">
<Settings className="size-5" />
</div>
<h3 className="mt-2 mb-3 text-lg font-semibold">
بهینه‌سازی برای توانمندسازی
</h3>
<p className="text-muted-foreground">
ما باور داریم که همه باید توانمند باشند تا هر کاری را که به نظرشان در راستای منافع شرکت است انجام دهند.
</p>
</div>
</div>
</div>
</div>
</section>
);
};
export { About1 };
+118
View File
@@ -0,0 +1,118 @@
import { ArrowUpRight } from "lucide-react";
import { cn } from "@/lib/utils";
const profile = {
title: "کسب‌وکار نوپا سامانه ابری",
subtitle: "ساخت آینده بهره‌وری",
bio: `تیمی پرشور از نوآوران هستیم که راهکارهای SaaS پیشرفته می‌سازیم تا کسب‌وکارها عملیات خود را ساده‌تر کنند و بهره‌وری را بالا ببرند. مأموریت ما نرم‌افزاری است که نه‌تنها مشکلات امروز را حل کند، بلکه چالش‌های فردا را پیش‌بینی کند.`,
vision: `به قدرت فناوری برای تحول نحوه کار کسب‌وکارها باور داریم. هدف ما ساخت راهکارهای شهودی و مقیاس‌پذیر است که با مشتریان رشد کند و به نیازهای در حال تغییر آن‌ها سازگار شود.`,
outro: `به هزاران کسب‌وکاری بپیوندید که به پلتفرم ما برای موفقیت خود اعتماد دارند. بیایید با هم چیزی شگفت‌انگیز بسازیم.`,
team: [
{ id: "01", item: "مریم چناری", type: "مدیرعامل و هم‌بنیان‌گذار" },
{ id: "02", item: "مهدی محمدی", type: "مدیر فناوری و هم‌بنیان‌گذار" },
{ id: "03", item: "الهام حسینی", type: "سرپرست محصول" },
{ id: "04", item: "داوود کیمیایی", type: "مهندس ارشد" },
{ id: "05", item: "لیلا احمدی", type: "سرپرست طراحی" },
{ id: "06", item: "امیر جعفری", type: "سرپرست بازاریابی" },
{ id: "07", item: "راحله سبز", type: "سرپرست فروش" },
],
};
interface About10Props {
className?: string;
}
const About10 = ({ className }: About10Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="flex flex-col-reverse gap-5 md:flex-row md:gap-12">
<div className="w-full md:w-1/3 md:pr-4">
<div className="sticky top-20 md:p-6">
<div className="mb-8">
<div className="mb-6 flex items-center gap-4">
<img
src="/block-media/shadcnblocks/block/photos/futuristic-device-design-qcufu.png"
alt="تیم کسب‌وکار نوپا سامانه ابری"
className="h-16 w-16 rounded-lg object-cover"
/>
<div>
<h3 className="font-semibold">{profile.title}</h3>
<p className="text-sm text-muted-foreground">
{profile.subtitle}
</p>
</div>
</div>
</div>
<nav>
<div className="py-4">
<div className="group flex p-0 text-start text-xl font-medium hover:bg-transparent sm:text-xl">
<span className="border-b-2 border-border pb-0.5 transition-colors">
تماس با ما
</span>
<ArrowUpRight className="ml-1 h-6 w-5 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</div>
</div>
</nav>
</div>
</div>
<div className="w-full md:w-2/3 md:p-6">
<div className="max-w-4xl">
<h1 className="mb-12 text-2xll font-semibold">درباره ما</h1>
<div className="space-y-12">
<p className="w-full text-xl leading-[36px] font-medium md:max-w-2xl">
{profile.bio}
</p>
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2">
<h2 className="text-xl font-medium">فلسفه اصلی</h2>
<p className="leading-relaxed">{profile.vision}</p>
</div>
<div className="my-12">
<img
src="/block-media/shadcnblocks/block/lummi/bw12.jpeg"
alt="دفتر کسب‌وکار نوپا سامانه ابری"
className="rounded-2xl object-cover"
/>
</div>
<div>
<h2 className="mb-8 text-xl font-medium">تیم ما</h2>
<div className="space-y-6">
{profile.team.map(({ id, item, type }) => (
<div
key={id}
className="flex items-center justify-between border-b py-4"
>
<div className="flex items-center gap-4">
<span className="font-mono text-sm text-muted-foreground">
{id}
</span>
<span className="text-base">{item}</span>
</div>
<span className="text-sm text-muted-foreground">
{type}
</span>
</div>
))}
</div>
</div>
<div className="mt-16">
<p className="leading-relaxed">{profile.outro}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export { About10 };
+231
View File
@@ -0,0 +1,231 @@
import React from "react";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
const awards = [
{
title: "جایزه رهبری طراحی",
year: "2025",
},
{
title: "بهترین بازطراحی برند",
year: "2025",
},
{
title: "جایزه تعالی تجربه کاربری",
year: "2024",
},
{
title: "نوآوری در هویت بصری",
year: "2024",
},
{
title: "تقدیر طراحی بسته‌بندی",
year: "2023",
},
{
title: "اثر اجتماعی در طراحی",
year: "2023",
},
];
interface About12Props {
className?: string;
}
const About12 = ({ className }: About12Props) => {
return (
<div className={cn("py-32", className)}>
<div className="container">
<section className="grid grid-cols-1 items-start gap-6 md:grid-cols-6">
<div className="col-span-1 flex items-center gap-1 md:col-span-2">
<span className="size-2 bg-primary" />
<h1 className="font-mono text-sm text-muted-foreground">درباره</h1>
</div>
<div className="col-span-1 flex flex-col gap-8 md:col-span-4 md:gap-24">
<p className="text-xl font-medium tracking-tight text-balance md:text-2xll">
پلتفرم‌های CRM و بازاریابی می‌سازیم که ارزش پیشنهادی شما را
به مشتریان هدف منتقل می‌کنند و رشد درآمد را تسریع می‌بخشند.
</p>
<div className="flex flex-col gap-4 md:gap-6">
<img
src="/block-media/shadcnblocks/block/photos/annie-spratt-MChSQHxGZrQ-unsplash.jpg"
alt="پرتره"
className="aspect-[6/8] rounded-xl object-cover md:max-w-md"
/>
<p className="max-w-md text-muted-foreground">
فراتر از تحویل پروژه، به ایجاد همکاری‌های بلندمدت باور داریم. از هر تجربه می‌آموزیم و ایده‌های کوچک را به تغییرات ماندگار تبدیل می‌کنیم.
</p>
</div>
</div>
</section>
<Separator className="my-16 md:my-32" />
<section className="space-y-6">
<div className="flex items-center gap-1">
<span className="size-2 bg-primary" />
<h1 className="font-mono text-sm text-muted-foreground">فرایند</h1>
</div>
<div className="grid gap-6 md:grid-cols-3">
<div className="flex flex-col gap-4">
<div className="relative overflow-hidden rounded-xl border">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="جای‌نگهدار"
className="aspect-square object-cover dark:invert"
/>
<span className="absolute top-3 start-3 rounded-xs bg-primary px-2.5 py-1 font-mono text-sm text-primary-foreground">
01
</span>
</div>
<div className="flex flex-col gap-1 md:gap-2">
<h3 className="text-xl font-medium lg:text-xl">درک</h3>
<p className="text-sm text-muted-foreground lg:text-base">
با پژوهش و گفت‌وگو شروع می‌کنیم تا اهداف، مخاطب و محدودیت‌ها
روشن شود.
</p>
</div>
</div>
<div className="flex flex-col gap-4">
<div className="relative overflow-hidden rounded-xl border">
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="جای‌نگهدار"
className="aspect-square object-cover dark:invert"
/>
<span className="absolute top-3 left-3 rounded-xs bg-primary px-2.5 py-1 font-mono text-sm text-primary-foreground">
02
</span>
</div>
<div className="flex flex-col gap-1 md:gap-2">
<h3 className="text-xl font-medium lg:text-xl">طراحی</h3>
<p className="text-sm text-muted-foreground lg:text-base">
مسیرها و نمونه‌های اولیه در هویت، محصول و محتوا با نقاط
کنترل شفاف می‌سازیم.
</p>
</div>
</div>
<div className="flex flex-col gap-4">
<div className="relative overflow-hidden rounded-xl border">
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="جای‌نگهدار"
className="aspect-square object-cover dark:invert"
/>
<span className="absolute top-3 left-3 rounded-xs bg-primary px-2.5 py-1 font-mono text-sm text-primary-foreground">
03
</span>
</div>
<div className="flex flex-col gap-1 md:gap-2">
<h3 className="text-xl font-medium lg:text-xl">تحویل</h3>
<p className="text-sm text-muted-foreground lg:text-base">
مستندات، راهنماها و پشتیبانی مستمر را فراهم می‌کنیم تا رشد و توسعه کسب‌وکار شما بدون مانع ادامه پیدا کند.
</p>
</div>
</div>
</div>
</section>
<Separator className="my-16 md:my-32" />
<section className="grid grid-cols-1 items-start gap-6 md:grid-cols-6">
<div className="col-span-1 flex items-center gap-1 md:col-span-2">
<span className="size-2 bg-primary" />
<h1 className="font-mono text-sm text-muted-foreground">
نقاط عطف
</h1>
</div>
<div className="col-span-1 flex flex-col gap-6 md:col-span-4">
<div className="flex flex-col justify-between gap-2 rounded-lg bg-muted p-8 lg:flex-row lg:items-center lg:gap-10 lg:p-12">
<span className="text-2xll font-medium lg:text-2xll">120+</span>
<p className="text-primary/80 lg:max-w-md">
پروژه‌های موفق در صنایع مختلف، از غذا و نوشیدنی تا فین‌تک.
</p>
</div>
<div className="flex flex-col justify-between gap-2 rounded-lg bg-muted p-8 lg:flex-row lg:items-center lg:gap-10 lg:p-12">
<span className="text-2xll font-medium lg:text-2xll">12+</span>
<p className="text-primary/80 lg:max-w-md">
سال‌ها تجربه در توسعه برند، بهبود فرایندها و طراحی سیستم‌های پایدار.
</p>
</div>
<div className="flex flex-col justify-between gap-2 rounded-lg bg-muted p-8 lg:flex-row lg:items-center lg:gap-10 lg:p-12">
<span className="text-2xll font-medium lg:text-2xll">15+</span>
<p className="text-primary/80 lg:max-w-md">
جوایز و افتخاراتی که حاصل اعتماد مشتریان و کیفیت همکاری‌های ما هستند.
</p>
</div>
</div>
</section>
<img
src="/block-media/shadcnblocks/block/photos/studio-republic-fotKKqWNMQ4-unsplash.jpg"
alt="جای‌نگهدار"
className="my-16 aspect-square w-full rounded-xl object-cover md:my-32 md:aspect-video"
/>
<section className="grid grid-cols-1 items-start gap-6 md:grid-cols-6">
<div className="col-span-1 flex items-center gap-1 md:col-span-2">
<span className="size-2 bg-primary" />
<h1 className="font-mono text-sm text-muted-foreground">جوایز</h1>
</div>
<div className="col-span-1 flex flex-col gap-4 md:col-span-4">
<Separator />
{awards.map((award, index) => (
<React.Fragment key={index}>
<div key={index} className="flex items-center justify-between">
<p className="font-medium tracking-tight md:text-lg">
{award.title}
</p>
<p className="font-mono text-sm font-medium text-muted-foreground md:text-base">
{award.year}
</p>
</div>
<Separator />
</React.Fragment>
))}
</div>
</section>
<Separator className="my-16 md:my-32" />
<section className="grid grid-cols-1 items-start gap-6 md:grid-cols-6">
<div className="col-span-1 flex items-center gap-1 md:col-span-2">
<span className="size-2 bg-primary" />
<h1 className="font-mono text-sm text-muted-foreground">
نظرات
</h1>
</div>
<div className="flex flex-col gap-8 md:col-span-4 md:gap-12">
<div className="flex flex-col gap-4">
<p className="text-xl font-medium tracking-tight md:text-2xll">
&ldquo;برند و محصول ما را هم‌راستا کردند و اثر آن فوراً در
تیم‌ها و مشتریان ما دیده شد.&rdquo;
</p>
<p className="font-medium text-muted-foreground md:text-lg">
نگار، مدیر عملیات در یک خرده‌فروشی جهانی
</p>
</div>
<div className="flex flex-col gap-2">
<a href="#" className="group relative overflow-hidden rounded-lg">
<img
src="/block-media/shadcnblocks/block/photos/israel-andrade-YI_9SivVt_s-unsplash.jpg"
alt="جای‌نگهدار"
className="transition-scale aspect-video scale-103 object-cover duration-400 group-hover:scale-101"
/>
<div className="absolute inset-0 hidden bg-gradient-to-t from-black via-transparent to-transparent opacity-0 transition-all duration-400 group-hover:opacity-60 md:block" />
<div className="absolute bottom-5 left-5 hidden translate-y-16 flex-col transition-transform duration-400 group-hover:translate-y-0 md:flex">
<p className="text-lg font-medium text-white">مطالعه موردی</p>
<p className="text-sm font-medium text-white/70">
بازطراحی تجارت الکترونیک
</p>
</div>
</a>
<div className="flex flex-col md:hidden">
<p className="font-medium">مطالعه موردی</p>
<p className="text-sm font-medium text-muted-foreground">
بازطراحی تجارت الکترونیک
</p>
</div>
</div>
</div>
</section>
</div>
</div>
);
};
export { About12 };
+63
View File
@@ -0,0 +1,63 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About13Props {
className?: string;
}
const About13 = ({ className }: About13Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container space-y-10 lg:space-y-20">
<div className="w-full grid-cols-6 gap-10 lg:grid">
<div />
<h1 className="col-span-4 text-2xll font-semibold tracking-tighter lg:pr-24 lg:pl-10 lg:text-8xl">
درباره ما
</h1>
</div>
<div className="grid-cols-6 space-y-12 lg:grid lg:space-y-0 xl:gap-10">
<p className="hidden text-foreground/40 lg:block">
تیم ما، مأموریت ما
</p>
<div className="col-span-2 lg:pr-24 lg:pl-10">
<p className="text-foreground/40">
می‌خواهیم ذهن‌های متنوع را کنار هم بیاوریم و ایده‌ها را به
تجربه‌هایی تبدیل کنیم که اهمیت دارند.
</p>
<div className="mt-5 flex items-center gap-5 lg:mt-20">
<img
src="/block-media/shadcnblocks/block/guri3/avatar1.png"
className="size-12"
alt="آواتار"
/>
<div>
<h3 className="text-lg font-medium tracking-tight">علی رضایی</h3>
<p className="text-sm text-foreground/40">مدیر خلاقیت</p>
</div>
</div>
</div>
<div className="col-span-3 mt-32 lg:mt-0 lg:pl-10">
<h2 className="text-xl font-medium tracking-tight lg:text-3xl">
ما تیمی از متخصصان محصول، فروش و بازاریابی هستیم که به ساخت
پلتفرم‌های B2B مؤثر باور داریم. مأموریت ما بر نوآوری،
کارایی و کمک به رشد درآمد تیم‌های فروش بنا شده است.
</h2>
<p className="mt-6 text-base text-foreground/40 lg:mt-18 lg:text-lg">
همیشه از خود می‌پرسیم: هدف چیست؟ مخاطب کیست؟ و چگونه می‌توان تجربه‌ای ساده‌تر و بهتر خلق کرد؟
</p>
</div>
</div>
<div>
<img
src="/block-media/shadcnblocks/block/guri4/img10.png"
alt="تصویر درباره ما"
className="mt-4 h-150 w-full object-cover"
/>
</div>
</div>
</section>
);
};
export { About13 };
+58
View File
@@ -0,0 +1,58 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About14Props {
className?: string;
}
const About14 = ({ className }: About14Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container space-y-10 lg:space-y-20">
<div className="w-full grid-cols-6 gap-10 space-y-5 lg:grid lg:space-y-0">
<h1 className="col-span-2 text-2xll font-semibold tracking-tight lg:text-2xll">
درباره ما
</h1>
<div />
</div>
<div>
<img
src="/block-media/shadcnblocks/block/guri4/img1.png"
alt="تصویر درباره ما"
className="mt-4 h-132 w-full object-cover"
/>
</div>
<div className="grid grid-cols-1 gap-10 space-y-12 lg:grid-cols-6 lg:space-y-0">
<p className="hidden text-foreground lg:block">تیم ما، مأموریت ما</p>
<div className="order-2 col-span-2 lg:order-none lg:pr-24 lg:pl-10">
<p className="text-foreground/40">
می‌خواهیم ذهن‌های متنوع را کنار هم بیاوریم و ایده‌ها را به
تجربه‌هایی تبدیل کنیم که اهمیت دارند.
</p>
<div className="mt-5 flex items-center gap-5 lg:mt-12">
<img
src="/block-media/shadcnblocks/block/guri3/avatar1.png"
className="size-12"
alt="آواتار"
/>
<div>
<h3 className="text-lg font-medium tracking-tight">علی رضایی</h3>
<p className="text-sm text-foreground/40">مدیر خلاقیت</p>
</div>
</div>
</div>
<div className="order-1 col-span-3 lg:order-none lg:mt-0 lg:pl-10">
<h2 className="text-xl font-medium tracking-tight lg:text-3xl">
ما تیمی از متخصصان محصول، فروش و بازاریابی هستیم. پلتفرم‌های
B2B می‌سازیم که به تیم‌ها کمک می‌کند درآمد را افزایش دهند.
مأموریت ما بر نوآوری، کارایی و رشد پایدار مشتریان بنا شده است.
</h2>
</div>
</div>
</div>
</section>
);
};
export { About14 };
+47
View File
@@ -0,0 +1,47 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About15Props {
className?: string;
}
const About15 = ({ className }: About15Props) => {
return (
<section className={cn("bg-neutral-950 py-32 text-white", className)}>
<div className="container flex flex-col items-center justify-center gap-25 lg:flex-row lg:gap-[10vw]">
<div className="w-xs rotate-[-6deg] border bg-white p-1">
<img
src="/block-media/shadcnblocks/block/guri4/img14.png"
alt=""
className="pointer-events-none h-110 w-full object-cover"
/>
<div className="pt-2 pb-1">
<p className="text-lg font-medium tracking-tight text-neutral-950">
علی رضایی
</p>
<p className="text-sm text-neutral-950/50">@ری یو آی</p>
</div>
</div>
<div className="w-xs space-y-6">
<h1 className="mb-15 text-2xll font-medium tracking-tight">
به ایده‌ها وضوح و اثر می‌بخشیم{" "}
<span className="font-serif"></span> شکل می‌دهیم
</h1>
<p className="text-sm lg:text-base">
هر پروژه با پرسش ساده آغاز می‌شود: چه چیزی باید حس شود و چه
اثری باید بگذارد؟ از آنجا مسیر را با دقت و شفافیت ترسیم
می‌کنیم.
</p>
<p className="text-sm lg:text-base">
بین استراتژی و اجرا پل می‌زنیم؛ طراحی را نه فقط زیبا، بلکه
کاربردی، قابل فهم و هم‌راستا با اهداف واقعی شما می‌سازیم.
</p>
</div>
</div>
</section>
);
};
export { About15 };
+71
View File
@@ -0,0 +1,71 @@
import React from "react";
import { cn } from "@/lib/utils";
const stats = [
{
number: "80K+",
description:
"از دسترسی به بیش از ۸۰هزار کاربر تا پشتیبانی از بیش از ۲۰ استارتاپ؛ این اعداد مقیاس، اثر و ثبات کار من را نشان می‌دهند.",
},
{
number: "20+",
description:
"استارتاپ‌هایی که با راهکارهای نوآورانه و راهنمایی استراتژیک همراه شدند تا در بازار خود مقیاس بگیرند و موفق شوند.",
},
{
number: "95%",
description:
"نرخ رضایت مشتری با پشتیبانی متعهد، تحویل باکیفیت و مشارکت‌های بلندمدت مبتنی بر اعتماد.",
},
];
interface About16Props {
className?: string;
}
const About16 = ({ className }: About16Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 gap-20 lg:grid-cols-6">
<div className="top-10 col-span-2 flex h-fit w-fit items-center gap-3 lg:sticky">
<span className="size-2 bg-orange-500" />
<p className="text-foreground/30 uppercase">چرا ما؟</p>
</div>
<div className="col-span-4 ml-auto max-w-3xl space-y-10">
<h1 className="text-3xl font-medium tracking-tight lg:text-xl">
«ما تیمی از متخصصان محصول و فروش هستیم. پلتفرم‌های B2B می‌سازیم
که به تیم‌ها کمک می‌کند درآمد را افزایش دهند. مأموریت ما بر
نوآوری، کارایی و رشد پایدار مشتریان بنا شده است.»
</h1>
<p className="text-lg text-foreground/40">
می‌پرسیم: هدف چیست؟ برای چه کسی است؟ چطور آن را بی‌دردسر کنیم؟
<br className="hidden lg:block" />
از چرایی، برای چه کسی و چگونه بهتر شدنش شروع می‌کنیم.
</p>
<img
src="/block-media/shadcnblocks/block/guri4/img11.png"
alt=""
className="pointer-events-none h-110 w-full object-cover"
/>
<ul className="mt-14">
{stats.map((stat, index) => (
<li key={index} className="grid grid-cols-5 border-b py-8">
<h3 className="col-span-2 text-xl font-medium">
{stat.number}
</h3>
<p className="col-span-3">{stat.description}</p>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
);
};
export { About16 };
+98
View File
@@ -0,0 +1,98 @@
"use client";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
interface About17Props {
className?: string;
}
const About17 = ({ className }: About17Props) => {
const [active, setActive] = useState<string>("اطلاعات");
const LINKS = [
{
title: "اطلاعات",
href: "#",
img: "/block-media/shadcnblocks/block/guri4/img11.png",
},
{
title: "کارها",
href: "#",
img: "/block-media/shadcnblocks/block/guri4/img9.png",
},
{
title: "مجله",
href: "#",
img: "/block-media/shadcnblocks/block/guri4/img10.png",
},
{
title: "آرشیو",
href: "#",
img: "/block-media/shadcnblocks/block/guri4/img6.png",
},
];
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 gap-15 lg:grid-cols-7 lg:gap-12">
<div className="top-10 col-span-3 h-fit w-fit text-xl tracking-tight">
<p className="font-medium">طراح مستقل و مهندس بنیان‌گذار</p>
<div className="mt-4 flex items-center gap-6 text-foreground/30">
<p>
{new Date().toLocaleTimeString("en-US", {
timeZone: "America/Chicago",
hour: "2-digit",
minute: "2-digit",
hour12: true,
})}
</p>
<p>CST</p>
</div>
</div>
<div className="col-span-4 ml-auto max-w-3xl space-y-10 lg:pl-15">
<h1 className="text-xl font-medium tracking-tight">
علی رضایی با ترکیب خلاقیت و تخصص فنی، محصولاتی دیجیتال خلق می‌کند که علاوه بر کارایی، تجربه‌ای لذت‌بخش برای کاربران فراهم می‌کنند. او با تکیه بر دانش توسعه توسعه کامل و نگاه دقیق به طراحی، ایده‌ها را از مرحله شکل‌گیری تا عرضه به محصولی مقیاس‌پذیر تبدیل می‌کند.
</h1>
</div>
</div>
<div className="mt-15 grid grid-cols-1 gap-4 border-t py-10 lg:grid-cols-7 lg:gap-12 lg:gap-15">
<ul className="top-10 order-2 col-span-3 flex h-fit w-fit gap-10 tracking-tight lg:order-none">
{LINKS.map((link) => (
<li
key={link.title}
className={cn(
"cursor-pointer",
active === link.title
? "text-foreground"
: "text-foreground/30",
)}
onClick={() => setActive(link.title)}
>
{link.title}
</li>
))}
</ul>
<ul className="order-1 col-span-4 ml-auto flex w-full max-w-3xl gap-10 font-medium tracking-tight text-foreground/40 lg:order-none lg:pl-15 lg:text-xl">
<li className="text-foreground">Austrlia</li>
<li>Brisbane</li>
<li>+1 (000) 023 0123</li>
</ul>
</div>
<div className="h-150">
<img
className="h-full w-full object-cover"
src={LINKS.find((link) => link.title === active)?.img}
alt=""
/>
</div>
</div>
</section>
);
};
export { About17 };
+58
View File
@@ -0,0 +1,58 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About18Props {
className?: string;
}
const About18 = ({ className }: About18Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 gap-15 lg:grid-cols-7 lg:gap-12">
<p className="col-span-2 font-medium">مأموریت ما</p>
<div className="col-span-4 ml-auto max-w-4xl space-y-10 lg:pl-15">
<h1 className="text-xl font-medium tracking-tight">
ایده‌ها را به پلتفرم‌های B2B مؤثر تبدیل می‌کنیم. هر پروژه فرصتی است تا با داده و اتوماسیون، رشد درآمد مشتریان را تسریع کنیم.
</h1>
<p className="w-fit text-lg text-foreground/40 lg:translate-y-2">
رویکرد ما بر پژوهش عمیق، طراحی هدفمند و اجرای باکیفیت استوار است. به همکاری، یادگیری مستمر و نوآوری باور داریم.
</p>
</div>
</div>
<div className="my-20 grid grid-cols-2 items-center gap-4">
<img
src="/block-media/shadcnblocks/block/guri4/img2.png"
alt=""
className="h-150 w-full object-cover saturate-0"
/>
<img
src="/block-media/shadcnblocks/block/guri4/img1.png"
alt=""
className="h-150 w-full object-cover"
/>
</div>
<div className="grid grid-cols-1 gap-15 lg:grid-cols-7 lg:gap-12">
<p className="col-span-2 font-medium">آنچه ما را به پیش می‌راند</p>
<div className="col-span-4 ml-auto max-w-4xl space-y-10 lg:pl-15">
<h1 className="text-xl font-medium tracking-tight">
ما تیمی از متخصصان محصول، فروش و بازاریابی هستیم. پلتفرم‌های
B2B می‌سازیم که به تیم‌ها کمک می‌کند درآمد را افزایش دهند.
مأموریت ما بر نوآوری، کارایی و رشد پایدار مشتریان بنا شده است.
</h1>
<p className="w-fit text-lg text-foreground/40 lg:translate-y-2">
همیشه از خود می‌پرسیم: هدف چیست، مخاطب کیست و چگونه می‌توان تجربه‌ای ساده‌تر و بهتر خلق کرد؟
<br className="hidden lg:block" />
همه‌چیز را از شناخت نیاز، مخاطب و بهترین راه‌حل آغاز می‌کنیم.
</p>
</div>
</div>
</div>
</section>
);
};
export { About18 };
+56
View File
@@ -0,0 +1,56 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About19Props {
className?: string;
}
const About19 = ({ className }: About19Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 gap-15 lg:grid-cols-7 lg:gap-1">
<div className="col-span-4 h-120 border">
<img
src="/block-media/shadcnblocks/block/guri4/img2.png"
alt=""
className="h-full w-full object-cover"
/>
</div>
<div className="col-span-3 ml-auto max-w-4xl space-y-15 lg:pl-15">
<h1 className="text-xl font-medium tracking-tight">
ما تیمی از متخصصان محصول، فروش و بازاریابی هستیم. پلتفرم‌های
B2B می‌سازیم که به تیم‌ها کمک می‌کند درآمد را افزایش دهند.
مأموریت ما بر نوآوری، کارایی و رشد پایدار مشتریان بنا شده است.
</h1>
<p className="text-base text-foreground/40 lg:text-lg">
همیشه از خود می‌پرسیم: هدف چیست، برای چه کسی طراحی می‌کنیم و چگونه می‌توان بهترین تجربه را ارائه داد؟
</p>
<div className="flex flex-col justify-between gap-4 lg:flex-row lg:items-center">
<p className="flex-1 text-sm text-foreground/40">
با گرد هم آوردن دیدگاه‌های متنوع، ایده‌ها را به تجربه‌هایی ارزشمند و تأثیرگذار تبدیل می‌کنیم.
</p>
<div className="flex w-fit items-center gap-2">
<img
src="/block-media/shadcnblocks/block/guri3/avatar1.png"
className="size-10"
alt="آواتار"
/>
<div>
<h3 className="font-medium tracking-tight">علی رضایی</h3>
<p className="text-sm text-foreground/40">
مدیر خلاقیت
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export { About19 };
+206
View File
@@ -0,0 +1,206 @@
import { cn } from "@/lib/utils";
interface About2Props {
className?: string;
}
const About2 = ({ className }: About2Props) => {
return (
<section className={cn("py-32", className)}>
<div className="flex flex-col gap-28">
<div className="container flex flex-col gap-10 text-center md:gap-24">
<div className="mx-auto flex max-w-3xl flex-col gap-6">
<h1 className="text-xl font-medium md:text-2xll">پشت صحنه ما</h1>
<p className="text-lg text-muted-foreground md:text-xl">
کشف کنید چگونه راهکار ما فرایندهای پیچیده را ساده می‌کند و مدیریت عملیات کلیدی و ارائه تجربه‌های استثنایی را آسان‌تر می‌سازد.
</p>
</div>
<div className="grid gap-6 md:grid-cols-12">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="placeholder"
className="size-full max-h-96 rounded-xl object-cover md:col-span-5"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="placeholder"
className="size-full max-h-96 rounded-xl object-cover md:col-span-4"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="placeholder"
className="size-full max-h-96 rounded-xl object-cover md:col-span-3"
/>
</div>
</div>
<div className="container flex flex-col gap-16">
<h2 className="max-w-3xl text-xl font-medium md:text-2xll">
ما در حوزه خود بهترین هستیم، اما مهارت تنها چیزی نیست که ارائه می‌کنیم.
</h2>
<div className="grid grid-cols-2 gap-6 md:grid-cols-3">
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">21M</p>
<p className="text-muted-foreground">گستره جهانی کاربران</p>
</div>
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">12+</p>
<p className="text-muted-foreground">سال‌ها تجربه و تخصص</p>
</div>
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">654</p>
<p className="text-muted-foreground">پروژه‌های تکمیل‌شده</p>
</div>
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">113k+</p>
<p className="text-muted-foreground">کاربران فعال ماهانه</p>
</div>
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">461k</p>
<p className="text-muted-foreground">حساب‌های ثبت‌شده</p>
</div>
<div className="flex flex-col gap-6 border-b pb-8">
<p className="text-xl font-medium md:text-2xll">98+</p>
<p className="text-muted-foreground">کاربران روزانه</p>
</div>
</div>
</div>
<div className="bg-muted/50 py-24">
<div className="container flex flex-col items-center gap-11">
<p className="text-center text-xl font-medium">
مورد اعتماد تیم‌های محصول پیشرو در سراسر جهان.
</p>
<div className="grid grid-cols-2 gap-x-7 gap-y-12 lg:grid-cols-4">
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-1.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">ری یو آی</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-2.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">Creative</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-3.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">Octan</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-4.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">Newco</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-5.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">ری یو آی</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-6.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">Fabrikam</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-1.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">ری یو آی</p>
</div>
<div className="flex items-center gap-3">
<img
src="/block-media/shadcnblocks/block/block-2.svg"
alt="logo"
className="h-8 w-auto md:h-14"
/>
<p className="text-xl font-semibold md:text-xl">ری یو آی</p>
</div>
</div>
</div>
</div>
<div className="container flex flex-col items-center gap-14">
<h2 className="text-center text-xl font-semibold md:text-2xll">
مزایای همکاری با ما را از نزدیک ببینید
</h2>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<div className="grid grid-cols-1 gap-6 md:col-span-2 md:grid-cols-2 md:flex-row lg:col-span-1 lg:grid-cols-1 lg:flex-col">
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="placeholder"
className="max-h-96 w-full rounded-xl object-cover"
/>
<div className="flex flex-col justify-center rounded-xl bg-muted p-8">
<p className="mb-2 text-xl font-medium">21M</p>
<p className="mb-6 font-semibold">کاربران جهانی</p>
<p className="text-muted-foreground">
با استفاده از ابزارهای ما، وظایف را ساده کرده و کارایی را تا ۸۰% افزایش دهید.
</p>
</div>
</div>
<div className="relative">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="placeholder"
className="h-full rounded-xl object-cover"
/>
<div className="absolute right-6 bottom-6 left-6 rounded-xl bg-background p-4">
<div className="mb-4 flex items-center gap-2">
<img
src="/block-media/shadcnblocks/block/block-1.svg"
alt="placeholder"
className="h-7 w-auto"
/>
<span className="text-lg font-semibold">ری یو آی.</span>
</div>
<p className="mb-6 text-sm">
راهکار ما با بهینه‌سازی فرایندها، هزینه‌های عملیاتی شما را به‌طور چشمگیری کاهش می‌دهد.
</p>
<div className="flex items-baseline gap-1">
<span className="font-medium">علی رضایی,</span>
<span className="text-sm text-muted-foreground">
مدیرعامل ری یو آی
</span>
</div>
</div>
</div>
<div className="flex flex-col gap-6">
<div className="rounded-xl bg-muted p-8">
<p className="mb-2 text-xl font-medium">97%</p>
<p className="mb-6 font-semibold">کاهش خطاها</p>
<p className="text-muted-foreground">
با جعبه‌ابزار پیشرفته ما به دقت و کارایی بیشتری دست پیدا کنید.
</p>
</div>
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="placeholder"
className="max-h-96 rounded-xl object-cover"
/>
</div>
</div>
</div>
</div>
</section>
);
};
export { About2 };
+149
View File
@@ -0,0 +1,149 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface Image {
src: string;
alt: string;
}
interface TextContent {
title?: string;
paragraphs: string[];
ctaButton?: {
href: string;
text: string;
};
}
interface About28Props {
leftImages?: Image[];
leftText?: TextContent;
rightText?: TextContent;
rightImages?: Image[];
className?: string;
}
const About28 = ({
leftImages = [
{
src: "/block-media/shadcnblocks/block/placeholder-1.svg",
alt: "همکاری تیم",
},
{
src: "/block-media/shadcnblocks/block/placeholder-2.svg",
alt: "فضای کاری تیم",
},
],
leftText = {
title: "تیم",
paragraphs: [
"ساخت را از ۲۱۹ آغاز کردیم و در ۲۲۲ عرضه کردیم. هر نقطه اتصال از صفر طراحی شده بدون بدهی فنی یا سیستم کهنه. برای نوآوری مدیریت پروژه در صد سال آینده ساخته شده‌ایم.",
"صددرصد در مالکیت بنیان‌گذاران و تیم است، سودآوریم و تیم را کوچک نگه می‌داریم. به‌مرور این صفحه کامل‌تر می‌شود، اما الان تمرکزمان بر تحویل به توسعه‌دهندگان است.",
"اگر به ساخت آینده مدیریت پروژه علاقه‌مندید، موقعیت‌های باز زیر را ببینید.",
],
ctaButton: {
href: "/careers",
text: "مشاهده موقعیت‌های شغلی",
},
},
rightText = {
paragraphs: [
"متعهدیم نحوه برنامه‌ریزی، اجرا و تحویل پروژه‌ها را متحول کنیم. مأموریت ما این است که به مشتریان مزیت بی‌رقیب در برابر تأخیر، ناکارآمدی و بی‌نظمی بدهیم از طریق بینش‌های عملی و همکاری بی‌درز.",
"مشتری‌محوریم زمان می‌گذاریم تا هر جنبه از گردش کار شما را بفهمیم و بهتر از همیشه کار کنید. همه با هم هستیم چون موفقیت شما، موفقیت ماست.",
],
},
rightImages = [
{
src: "/block-media/shadcnblocks/block/placeholder-3.svg",
alt: "فضای کاری مدرن",
},
{
src: "/block-media/shadcnblocks/block/placeholder-4.svg",
alt: "همکاری تیم",
},
],
className,
}: About28Props) => {
return (
<section
className={cn(
"container mt-10 flex max-w-5xl flex-col-reverse gap-8 md:mt-14 md:gap-14 lg:mt-20 lg:flex-row lg:items-end",
className,
)}
>
{/* Images Left - Text Right */}
<div className="flex flex-col gap-8 lg:gap-16 xl:gap-20">
<ImageSection images={leftImages} className="xl:-translate-x-10" />
<TextSection
title={leftText.title}
paragraphs={leftText.paragraphs}
ctaButton={leftText.ctaButton}
/>
</div>
{/* Text Left - Images Right */}
<div className="flex flex-col gap-8 lg:gap-16 xl:gap-20">
<TextSection paragraphs={rightText.paragraphs} />
<ImageSection
images={rightImages}
className="hidden lg:flex xl:translate-x-10"
/>
</div>
</section>
);
};
interface ImageSectionProps {
images: Image[];
className?: string;
}
const ImageSection = ({ images, className }: ImageSectionProps) => {
return (
<div className={cn("flex flex-col gap-6", className)}>
{images.map((image, index) => (
<div
key={index}
className="relative aspect-[2/1.5] overflow-hidden rounded-2xl"
>
<img
src={image.src}
alt={image.alt}
className="absolute inset-0 size-full object-cover"
/>
</div>
))}
</div>
);
};
interface TextSectionProps {
title?: string;
paragraphs: string[];
ctaButton?: {
href: string;
text: string;
};
}
const TextSection = ({ title, paragraphs, ctaButton }: TextSectionProps) => {
return (
<div className="flex-1 space-y-4 text-lg md:space-y-6">
{title && <h2 className="text-xl text-foreground">{title}</h2>}
<div className="max-w-xl space-y-6 text-muted-foreground">
{paragraphs.map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
{ctaButton && (
<div className="mt-8">
<Button size="lg" asChild>
<a href={ctaButton.href}>{ctaButton.text}</a>
</Button>
</div>
)}
</div>
);
};
export { About28 };
+220
View File
@@ -0,0 +1,220 @@
import {
Marquee,
MarqueeContent,
MarqueeFade,
MarqueeItem,
} from "@/components/kibo-ui/marquee";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface About3Props {
className?: string;
title: string;
description?: string;
mainImage: {
src: string;
alt: string;
};
secondaryImage: {
src: string;
alt: string;
};
breakout: {
src?: string;
alt?: string;
title: string;
description: string;
buttonText?: string;
buttonUrl?: string;
};
companies?: Array<{
src: string;
alt: string;
}> | null;
achievementsTitle?: string;
achievementsDescription?: string;
achievements?: Array<{
label: string;
value: string;
}>;
contentSections?: Array<{
title: string;
content: string;
}>;
}
const About3 = ({
className,
title = "درباره ما",
description = "ما تیمی پرشور هستیم که به خلق راهکارهای نوآورانه برای توانمندسازی کسب‌وکارها در عصر دیجیتال متعهدیم. با سال‌ها تجربه در طراحی و توسعه، مؤلفه‌هایی زیبا و در دسترس خلق می‌کنیم که به تیم‌ها کمک می‌کند سریع‌تر بسازند.",
mainImage = {
src: "/block-media/shadcnblocks/block/photos/annie-spratt-MChSQHxGZrQ-unsplash.jpg",
alt: "about",
},
secondaryImage = {
src: "/block-media/shadcnblocks/block/photos/annie-spratt-AkftcHujUmk-unsplash.jpg",
alt: "about",
},
breakout = {
src: "/block-media/shadcnblocks/block/block-1.svg",
alt: "logo",
title: "هزاران بلاک آماده در ری یو آی",
description:
"ارائه ابزارهای مؤثر به کسب‌وکارها برای بهبود روند کار، افزایش کارایی و تشویق رشد",
buttonText: "بیشتر بدانید",
buttonUrl: "https://www.ShadcnBlocks",
},
companies = [
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-1.svg",
alt: "Arc",
},
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-2.svg",
alt: "Descript",
},
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-3.svg",
alt: "Mercury",
},
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-4.svg",
alt: "Ramp",
},
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-5.svg",
alt: "Retool",
},
{
src: "/block-media/shadcnblocks/block/logos/company/fictional-company-logo-6.svg",
alt: "Watershed",
},
],
achievementsTitle = "دستاوردهای ما به عدد و رقم",
achievementsDescription = "ارائه ابزارهای مؤثر به کسب‌وکارها برای بهبود گردش‌کار، افزایش کارایی و تقویت رشد.",
achievements = [
{ label: "شرکت‌ها", value: "300+" },
{ label: "پروژه‌های تکمیل‌شده", value: "800+" },
{ label: "مشتریان خوشحال", value: "99%" },
{ label: "جوایز و افتخارات", value: "10+" },
],
contentSections = [
{
title: "دیدگاه ما",
content:
"سال‌هاست فرایند ساخت نرم‌افزار سفارشی دشوار مانده است؛ امروز ابزارهای بصری هست، اما راهکارهای دقیق همچنان به تخصص فنی و زمان زیاد نیاز دارند و این برای کسب‌وکارها و افراد دردساز است.\n\nاگر می‌توانستید بدون نوشتن حتی یک خط کد، نرم‌افزار سفارشی بسازید چه؟ اگر می‌توانستید ابزارهای خودتان را بسازید؟\n\nبا پلتفرم ما می‌توانید! ابزارهای ما به شما اجازه می‌دهند چیدمان طراحی کنید و قابلیت بسازیدهمه این‌ها بدون نیاز به کدنویسی.\n\nما باور داریم هر کسی باید بتواند راهکار خودش را بسازد، فارغ از پیشینه فنی.",
},
{
title: "سازندگان",
content:
"شرکت ما بیش از یک دهه است ابزارهای وب می‌سازد و در هر پروژه بر کارایی و کنترل کاربر تمرکز دارد. می‌دانیم بهترین راهکارها همان‌هایی‌اند که خودتان می‌توانید بسازید.\n\nابتدا این راهکارها را برای تیم خودمان توسعه دادیم و حالا همه می‌توانند از آن‌ها بهره ببرند. افتخار می‌کنیم پلتفرمی در دسترس همه ارائه می‌دهیم، بدون توجه به تخصص فنی.\n\nتیم ما از افراد بااستعداد و مشتاق ساخت ابزاری تشکیل شده که به کاربران قدرت می‌دهد راهکارهایشان را به‌سادگی بسازند. در دستیابی شما به اهدافتان کنار شما هستیم.",
},
],
}: About3Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="mb-14 flex flex-col gap-5 lg:w-2/3">
<h1 className="text-2xll font-semibold tracking-tighter lg:text-2xll">
{title}
</h1>
<p className="text-lg text-muted-foreground md:text-xl">
{description}
</p>
</div>
<div className="grid gap-7 lg:grid-cols-3">
<img
src={mainImage.src}
alt={mainImage.alt}
className="size-full max-h-[620px] rounded-xl object-cover lg:col-span-2"
/>
<div className="flex flex-col gap-7 md:flex-row lg:flex-col">
<div className="flex flex-col justify-between gap-6 rounded-xl bg-muted p-7 md:w-1/2 lg:w-auto">
<img
src={breakout.src}
alt={breakout.alt}
className="mr-auto h-12 dark:invert"
/>
<div>
<p className="mb-2 text-lg font-semibold">{breakout.title}</p>
<p className="text-muted-foreground">{breakout.description}</p>
</div>
<Button variant="outline" className="mr-auto" asChild>
<a href={breakout.buttonUrl} target="_blank">
{breakout.buttonText}
</a>
</Button>
</div>
<img
src={secondaryImage.src}
alt={secondaryImage.alt}
className="grow basis-0 rounded-xl object-cover md:w-1/2 lg:min-h-0 lg:w-auto"
/>
</div>
</div>
{companies && (
<div className="py-32">
<Marquee>
<MarqueeContent speed={40}>
{companies.map((company, idx) => (
<MarqueeItem
key={company.src + idx}
className="mx-8 flex items-center"
>
<img
src={company.src}
alt={company.alt}
className="h-7 w-auto md:h-8 dark:invert"
/>
</MarqueeItem>
))}
</MarqueeContent>
<MarqueeFade side="left" />
<MarqueeFade side="right" />
</Marquee>
</div>
)}
<div className="relative overflow-hidden rounded-xl bg-muted p-7 md:p-16">
<div className="flex flex-col gap-4 text-center md:text-start">
<h2
className="text-3xl font-medium md:text-xl text-end"
dir="rtl"
>
{achievementsTitle}
</h2>
<p className="max-w-xl text-muted-foreground">
{achievementsDescription}
</p>
</div>
<div className="mt-10 grid grid-cols-2 gap-x-4 gap-y-8 md:flex md:flex-wrap md:justify-between">
{achievements.map((item, idx) => (
<div
className="flex flex-col gap-2 text-center md:text-start"
key={item.label + idx}
>
<span className="font-mono text-xl font-semibold md:text-2xll">
{item.value}
</span>
<p className="text-sm md:text-base">{item.label}</p>
</div>
))}
</div>
</div>
{contentSections && contentSections.length > 0 && (
<div className="mx-auto grid max-w-5xl gap-16 py-28 md:grid-cols-2 md:gap-28">
{contentSections.map((section, idx) => (
<div key={section.title + idx}>
<h2 className="mb-5 text-xl font-medium">{section.title}</h2>
<p className="text-lg leading-7 whitespace-pre-line text-muted-foreground">
{section.content}
</p>
</div>
))}
</div>
)}
</div>
</section>
);
};
export { About3 };
+107
View File
@@ -0,0 +1,107 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface About4Props {
className?: string;
}
const About4 = ({ className }: About4Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="mx-auto flex max-w-3xl flex-col gap-8 pb-28 text-center">
<h1 className="text-xl font-semibold md:text-2xll">درباره ما</h1>
<p className="text-xl font-medium text-muted-foreground">
با تیم ما آشنا شوید، ارزش‌هایمان را بشناسید و ببینید چطور کار، زندگی و
همه‌چیز بین این دو را متعادل می‌کنیم.
</p>
</div>
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-4.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-5.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-6.svg"
alt="placeholder"
className="max-h-80 w-full object-cover"
/>
</div>
<div className="mx-auto grid max-w-5xl gap-28 py-28 md:grid-cols-2">
<div>
<h2 className="mb-5 text-xl font-semibold">چشم‌انداز ما</h2>
<p className="text-xl leading-8 font-medium text-muted-foreground">
سال‌هاست فرایند ساخت نرم‌افزار سفارشی دشوار مانده است؛ امروز ابزارهای
بصری هست، اما راهکارهای دقیق همچنان به تخصص فنی و زمان زیاد نیاز
دارند و این برای کسب‌وکارها و افراد دردساز است.
<br />
<br />
اگر می‌توانستید بدون نوشتن حتی یک خط کد، نرم‌افزار سفارشی بسازید
چه؟ اگر می‌توانستید ابزارهای خودتان را بسازید؟
<br />
<br />
با پلتفرم ما می‌توانید! ابزارهای ما به شما اجازه می‌دهند چیدمان
طراحی کنید و قابلیت بسازیدهمه این‌ها بدون نیاز به کدنویسی.
<br />
<br />
ما باور داریم هر کسی باید بتواند راهکار خودش را بسازد، فارغ از
پیشینه فنی.
</p>
</div>
<div>
<h2 className="mb-5 text-xl font-semibold">سازندگان</h2>
<p className="text-xl leading-8 font-medium text-muted-foreground">
<a href="#" className="mr-1 underline">
شرکت ما
</a>
بیش از یک دهه است ابزارهای وب می‌سازد و در هر پروژه بر کارایی و کنترل
کاربر تمرکز دارد. می‌دانیم بهترین راهکارها همان‌هایی‌اند که خودتان
می‌توانید بسازید.
<br />
<br />
ابتدا این راهکارها را برای تیم خودمان توسعه دادیم و حالا همه
می‌توانند از آن‌ها بهره ببرند. افتخار می‌کنیم پلتفرمی در دسترس همه
ارائه می‌دهیم، بدون توجه به تخصص فنی.
<br />
<br />
تیم ما از افراد بااستعداد و مشتاق ساخت ابزاری تشکیل شده که به
کاربران قدرت می‌دهد راهکارهایشان را به‌سادگی بسازند. در دستیابی شما
به اهدافتان کنار شما هستیم و مشتاقیم ببینیم چه می‌سازید!
</p>
</div>
</div>
<div className="mx-auto flex max-w-5xl flex-col items-center justify-between gap-8 rounded-2xl bg-muted/50 p-14 text-center md:flex-row md:text-start">
<h3 className="text-3xl font-semibold">
بخشی از
<br />
تیم جهانی ما
</h3>
<Button size="lg">آشنایی با تیم</Button>
</div>
</div>
</section>
);
};
export { About4 };
+108
View File
@@ -0,0 +1,108 @@
import { cn } from "@/lib/utils";
interface About5Props {
className?: string;
}
const About5 = ({ className }: About5Props) => {
return (
<section className={cn("bg-muted py-32 dark:bg-background", className)}>
<div className="container">
<div className="grid gap-14 pb-32 md:grid-cols-2">
<div>
<p className="text-sm font-medium">درباره ما</p>
<h1 className="mt-4 text-3xl font-medium md:text-xl">
ساده‌سازی گردش‌کارهای پیچیده با راهکارهای متمرکز بر توسعه‌دهنده
</h1>
</div>
<p className="md:text-lg">
پلتفرم ما ابزارهایی ساده و قدرتمند برای مدیریت و توسعه پروژه‌ها فراهم می‌کند؛ با یکپارچه‌سازی روان، امکانات کاربردی و تجربه‌ای یکپارچه برای تیم‌ها در هر اندازه.
</p>
</div>
<img
src="/block-media/shadcnblocks/block/placeholder-dark-1.svg"
alt="placeholder"
className="ml-auto aspect-video max-h-[550px] rounded-xl object-cover"
/>
<p className="mt-6 text-center text-xl lg:text-end">
متعهد به حذف پیچیدگی از فرایندهای توسعه
</p>
<div className="flex flex-col justify-between gap-14 py-40 lg:flex-row">
<p className="mx-auto max-w-xl text-center text-xl lg:mx-0 lg:text-start">
ما تیمی از توسعه‌دهندگان، طراحان و کارآفرینان پرشور هستیم.
</p>
<div className="grid shrink-0 grid-cols-2 items-center gap-6 md:grid-cols-4">
<img
src="/block-media/shadcnblocks/block/logos/vercel-wordmark.svg"
alt="logo"
className="mx-auto max-h-6 md:mx-0 dark:invert"
/>
<img
src="/block-media/shadcnblocks/block/logos/shadcn-ui-wordmark.svg"
alt="logo"
className="mx-auto max-h-9 md:mx-0 dark:invert"
/>
<img
src="/block-media/shadcnblocks/block/logos/tailwind-wordmark-light.svg"
alt="logo"
className="mx-auto max-h-7 md:mx-0 md:max-h-9 dark:hidden"
/>
<img
src="/block-media/shadcnblocks/block/logos/tailwind-wordmark-dark.svg"
alt="logo"
className="mx-auto hidden max-h-7 md:mx-0 md:max-h-9 dark:block"
/>
<img
src="/block-media/shadcnblocks/block/logos/supabase-wordmark.svg"
alt="logo"
className="mx-auto max-h-9 md:mx-0 dark:hidden"
/>
<img
src="/block-media/shadcnblocks/block/logos/supabase-wordmark-dark.svg"
alt="logo"
className="mx-auto hidden max-h-9 md:mx-0 dark:block"
/>
</div>
</div>
<div className="grid gap-14 lg:grid-cols-4 xl:grid-cols-5">
<div className="md:col-span-2 xl:col-span-3">
<h2 className="mb-10 text-xl font-medium">چرا شروع کردیم</h2>
<p className="text-lg">
چالش‌های توسعه‌دهندگان با زیرساخت‌های پیچیده را از نزدیک دیده‌ایم.
به‌جای تکرار همان کارها، خواستیم سیستمی بسازیم که کل گردش‌کار را از
ابتدا تا انتها ساده کند و توسعه‌دهندگان روی آنچه بهتر انجام می‌دهند
تمرکز کنند. پلتفرم ما برای همکاری و مقیاس‌پذاری کارآمد پروژه‌ها
طراحی شده است.
<br />
<br />
مأموریت ما ارائه ابزارهایی است که تیم‌ها بتوانند پروژه‌ها را
کارآمد بسازند و مقیاس دهند. باور داریم با برداشتن موانع ورود،
همکاری و بهره‌وری، تیم‌ها روی ساخت محصولات عالی تمرکز می‌کنند.
</p>
<div className="mt-6 grid grid-cols-2 gap-6 text-center">
<div className="flex flex-col items-center justify-center gap-2 rounded-xl border bg-background bg-card p-6">
<span className="text-xl md:text-xl">2024</span>
<span className="text-sm text-muted-foreground md:text-lg">
راه‌اندازی
</span>
</div>
<div className="flex flex-col items-center justify-center gap-2 rounded-xl border bg-background bg-card p-6">
<span className="text-xl md:text-xl">$2.2M</span>
<span className="text-sm text-muted-foreground md:text-lg">
مرحله پیش‌بذر سرمایه‌گذاری
</span>
</div>
</div>
</div>
<img
src="/block-media/shadcnblocks/block/placeholder-dark-1.svg"
alt="placeholder"
className="rounded-xl md:col-span-2"
/>
</div>
</div>
</section>
);
};
export { About5 };
+82
View File
@@ -0,0 +1,82 @@
import { cn } from "@/lib/utils";
interface About6Props {
className?: string;
}
const About6 = ({ className }: About6Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="flex flex-col items-center justify-start gap-6 lg:flex-row">
<div className="flex w-full flex-col items-start justify-start gap-24 lg:w-1/2">
<div className="pr-6">
<h1 className="mb-6 text-xl font-bold md:text-2xll lg:mb-10 lg:text-2xll">
درباره ما
</h1>
<p className="mb-9 text-muted-foreground lg:text-xl">
ما تیمی از نوآوران هستیم که با تکیه بر فناوری و خلاقیت، آینده را قدم‌به‌قدم می‌سازیم. مسیر ما از شناسایی یک نیاز واقعی آغاز شد و با ارائه راهکارهای نوآورانه ادامه پیدا کرد.
</p>
</div>
<div className="flex flex-col items-center justify-center gap-6 md:flex-row">
<img
src="/block-media/shadcnblocks/block/photos/annie-spratt-MChSQHxGZrQ-unsplash.jpg"
alt="درباره ۱"
className="aspect-[0.7] w-full rounded-lg object-cover md:w-1/2"
/>
<div className="flex w-full flex-col items-center justify-center gap-6 md:w-1/2">
<img
src="/block-media/shadcnblocks/block/photos/annie-spratt-AkftcHujUmk-unsplash.jpg"
alt="درباره ۲"
className="aspect-[1.1] rounded-lg object-cover"
/>
<img
src="/block-media/shadcnblocks/block/photos/annie-spratt-vGgn0xLdy8s-unsplash.jpg"
alt="درباره ۳"
className="aspect-[0.7] rounded-lg object-cover"
/>
</div>
</div>
</div>
<div className="flex w-full flex-col items-center justify-center gap-12 pt-12 lg:w-1/2 lg:pt-48">
<div className="flex flex-col items-center justify-center gap-6 md:flex-row">
<img
src="/block-media/shadcnblocks/block/photos/johnson-wang-iI4sR_nkkbc-unsplash.jpg"
alt="درباره ۴"
className="aspect-[0.9] w-full rounded-lg object-cover md:w-1/2"
/>
<div className="flex w-full flex-col items-center justify-center gap-6 md:w-1/2">
<img
src="/block-media/shadcnblocks/block/photos/nastuh-abootalebi-eHD8Y1Znfpk-unsplash.jpg"
alt="درباره ۵"
className="aspect-[0.8] rounded-lg object-cover"
/>
<img
src="/block-media/shadcnblocks/block/photos/alvin-engler-bIhpiQA009k-unsplash.jpg"
alt="درباره ۶"
className="aspect-[0.9] rounded-lg object-cover"
/>
</div>
</div>
<div className="px-8">
<h1 className="mb-8 text-xl font-semibold lg:mb-6">
محل کار ما
</h1>
<p className="mb-9 lg:text-xl">
محیط کار ما به گونه‌ای طراحی شده است که خلاقیت را شکوفا کند، همکاری را تقویت کند و فضایی الهام‌بخش برای رشد ایده‌ها فراهم آورد.
</p>
<p className="text-muted-foreground">
از فضاهای باز همکاری تا گوشه‌های آرام تمرکز، هر بخش از دفتر با
دقت برای سبک‌های مختلف کار طراحی شده است. از راه دور هم
دوستانه‌ایم؛ اعضای تیم در سراسر جهان با هم هماهنگ کار می‌کنند
تا نتایجی استثنایی تحویل دهند.
</p>
</div>
</div>
</div>
</div>
</section>
);
};
export { About6 };
+113
View File
@@ -0,0 +1,113 @@
import { ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface About7Props {
className?: string;
}
const About7 = ({ className }: About7Props) => {
return (
<section
dir="rtl"
lang="fa"
className={cn(
"overflow-x-clip bg-[#E9EDE8] py-32 dark:bg-[#2F332E]",
className,
)}
>
<div className="container">
<div className="max-w-xl lg:-translate-x-32">
<h2 className="mb-4 text-xl font-semibold text-muted-foreground md:text-3xl">
درباره ما
</h2>
<h1 className="max-w-[600px] text-3xl leading-tight font-semibold tracking-tight text-balance md:text-xl lg:text-2xll">
ما ری یو آی هستیم؛ پلتفرمی متفاوت برای برنامه‌ریزی و مدیریت کار که به سازندگان کمک می‌کند آینده را از امروز بسازند.
</h1>
</div>
<div className="relative mt-12 flex gap-4">
<div className="max-lg:translate-x-20">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="فضای کاری مدرن با نمایشگر iMac"
width={800}
height={500}
/>
</div>
<div className="bottom-0 aspect-[1.5/1.4] w-[max(30vw,220px)] -left-10 translate-y-20 max-lg:absolute max-lg:border-[16px] max-lg:border-[#E9EDE8] lg:-translate-y-20 dark:max-lg:border-[#2F332E]">
<img
src="/block-media/shadcnblocks/block/placeholder-2.svg"
alt="فردی در حال کار با لپ‌تاپ"
className="h-full w-full object-cover"
/>
</div>
</div>
<div className="mt-28 max-w-xl lg:mt-10 lg:-translate-x-32">
<p className="text-lg leading-relaxed">
وظایف تغییر کرده‌اند، در حال تغییرند و همچنان به سمت بهتر پیش
می‌روند. زمان آن است که بهره‌وری کاربرمحور و اینترنت‌محور باشد؛
اما برای این کار به زیربنایی نو نیاز داریم.
</p>
</div>
<div className="mt-6 space-y-6 md:mt-8 md:space-y-8 lg:mt-10 lg:space-y-10">
<h2 className="max-w-xl text-xl font-semibold md:text-3xl lg:-translate-x-32">
همیشه می‌گفتند ابزارهای مدیریت کار نمی‌توانند واقعاً پویا و انعطاف‌پذیر باشند.
</h2>
<p className="max-w-xl text-lg leading-relaxed lg:-translate-x-32">
سال‌ها ابزارهایی مانند مدیریت وظایف، یادداشت‌برداری و مدیریت وظایف را بررسی و استفاده کرده‌ایم. با وجود امکانات فراوان، هنوز نیاز به راهکاری ساده‌تر و هوشمندتر احساس می‌شود. ما باور داریم ابزارهای مدیریت کار باید پویا، منعطف و متناسب با نیاز کاربران باشند؛ به همین دلیل همه‌چیز را از ابتدا طراحی کردیم.
</p>
<div className="grid gap-6 py-6 md:grid-cols-2 lg:py-10">
<img
src="/block-media/shadcnblocks/block/placeholder-3.svg"
alt="همکاری اعضای تیم"
width={600}
height={400}
className="rounded-lg"
/>
<img
src="/block-media/shadcnblocks/block/placeholder-4.svg"
alt="جلسه تیم در اتاق کنفرانس"
width={600}
height={400}
className="rounded-lg"
/>
</div>
<p className="ms-auto max-w-xl text-lg leading-relaxed lg:translate-x-32">
توسعه ری یو آی را از سال ۲۱۹ آغاز کردیم و در سال ۲۲۲ نخستین نسخه آن را منتشر کردیم. هر قابلیت از پایه طراحی شده تا تجربه‌ای سریع، ساده و بدون پیچیدگی ارائه دهد.
</p>
<h2 className="ms-auto max-w-xl text-xl font-semibold md:text-3xl lg:translate-x-32">
تیمی کمی غیرمعمول هستیم نه استارتاپ فناوری معمولی.
</h2>
<div className="ms-auto max-w-xl text-lg leading-relaxed lg:translate-x-32">
<p>
صددرصد در مالکیت بنیان‌گذاران و تیم است، پایدار کار می‌کنیم و
تیم را کوچک نگه می‌داریم. به‌مرور این صفحه را کامل‌تر می‌کنیم،
اما الان تمرکزمان بر تحویل به کاربران است. اگر می‌خواهید همکاری
کنید، موقعیت‌های باز ما را ببینید:
</p>
<Button
variant="outline"
size="lg"
className="mt-6 md:mt-8 lg:mt-10"
>
<a href="#">
<span className="flex items-center gap-2 whitespace-pre-wrap">
مشاهده موقعیت‌های شغلی
<ArrowLeft className="size-4" />
</span>
</a>
</Button>
</div>
</div>
</div>
</section>
);
};
export { About7 };
+235
View File
@@ -0,0 +1,235 @@
import { type SVGProps, useId } from "react";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
interface About8Props {
className?: string;
}
const contentShell = "container max-w-5xl";
const proseStack = "space-y-5 md:space-y-8 lg:space-y-10";
const textInsetStart = "ps-6 md:ps-10 lg:ps-12";
const textFromStart = cn(textInsetStart, "max-w-2xl w-full");
const About8 = ({ className }: About8Props) => {
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
{/* Hero Section */}
<section
className={cn("relative py-10 md:py-12 lg:py-15", contentShell)}
>
<div className={textFromStart}>
<h1 className="text-xl font-semibold tracking-tight md:text-2xll lg:text-2xll">
بانکی،
<br />
متفاوت
</h1>
<p className="mt-4 text-xl text-muted-foreground md:text-3xl">
مأموریت ما ایجاد تحول در خدمات مالی با بهره‌گیری از ظرفیت عظیم داده‌های مالی بلااستفاده است.
</p>
</div>
{/* Background decoration */}
<>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_80%)]">
<PlusSigns className="h-full w-full text-foreground/[0.05]" />
</div>
</>
</section>
{/* Stats Section */}
<section className={cn("border-y py-5", contentShell)}>
<div className={textInsetStart}>
<h2 className="font-mono text-sm font-semibold tracking-widest text-accent-foreground">
در یک نگاه
</h2>
<div className="mt-6 grid grid-cols-2 gap-8 md:grid-cols-4">
<div>
<h3 className="text-xl font-medium tracking-tight md:text-2xll lg:text-2xll">
۱۵۰ هزار ریال
</h3>
<p className="mt-1 font-medium text-muted-foreground">
سرمایه جذب‌شده
</p>
</div>
<div>
<h3 className="text-xl font-medium tracking-tight md:text-2xll lg:text-2xll">
۲۰ هزار
</h3>
<p className="mt-1 font-medium text-muted-foreground">شرکت</p>
</div>
<div>
<h3 className="text-xl font-medium tracking-tight md:text-2xll lg:text-2xll">
1.3B
</h3>
<p className="mt-1 font-medium text-muted-foreground">
تراکنش ماهانه
</p>
</div>
<div>
<h3 className="text-xl font-medium tracking-tight md:text-2xll lg:text-2xll">
1.5K
</h3>
<p className="mt-1 font-medium text-muted-foreground">
اتصال در دقیقه
</p>
</div>
</div>
</div>
</section>
{/* Mission Section */}
<section className={cn("py-10 md:py-12 lg:py-15", contentShell)}>
<div className={cn(textFromStart, proseStack)}>
<p className="text-lg">
خدمات مالی در حال تحول‌اند. اکنون زمان آن رسیده است که زیرساخت‌های مالی سازنده محصول‌محور و مبتنی بر اتصال هوشمند باشند؛ اما این تحول به معماری و زیرساختی نو نیاز دارد.
</p>
<h2 className="text-xl font-medium tracking-tight md:text-3xl">
همیشه به ما گفته بودند بانک‌ها نمی‌توانند پلتفرم باشند.
</h2>
<p className="text-lg">
سال‌ها تلاش شد با افزودن لایه‌های اتصال هوشمند به سامانه‌های قدیمی، مشکلات برطرف شود؛ اما نتیجه چیزی جز پیچیدگی و ناکارآمدی نبود. تجربه کار با پلتفرم‌هایی مانند سامانه مالی، درگاه پرداخت و اعتبار خرید نشان داد که راهکارهای فعلی پاسخگوی نیاز آینده نیستند. ما باور داریم زیرساخت بانکی باید از پایه و با رویکرد اتصال‌محور بازطراحی شود.
</p>
</div>
</section>
{/* Image Grid Section */}
<section className="my-5 pb-10 md:my-8 md:pb-12 lg:my-12 lg:pb-15">
<Carousel
opts={{
align: "start",
direction: "rtl",
}}
>
<CarouselContent className="-mr-4 ml-0">
<CarouselItem className="basis-[80%] pl-0 pr-4 lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px] xl:h-[600px]">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="عضو تیم در حال کار"
className="object-cover"
/>
</div>
</CarouselItem>
<CarouselItem className="basis-[80%] pl-0 pr-4 lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px] xl:h-[600px]">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="فضای کاری مدرن"
className="object-cover"
/>
</div>
</CarouselItem>
<CarouselItem className="basis-[80%] pl-0 pr-4 lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px] xl:h-[600px]">
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="همکاری تیم"
className="object-cover"
/>
</div>
</CarouselItem>
</CarouselContent>
</Carousel>
</section>
{/* CoreAPI Section */}
<section className={contentShell}>
<div className={cn("ms-auto me-0 max-w-2xl w-full", proseStack)}>
<p className="text-lg">
توسعه ری یو آی را از سال ۲۱۹ آغاز کردیم و در سال ۲۲۲ نخستین نسخه آن را منتشر کردیم. تمام نقطه‌های اتصال از ابتدا طراحی شده‌اند؛ بدون بدهی فنی و وابستگی به سامانه‌های قدیمی، تا زیرساختی ماندگار برای آینده خدمات مالی فراهم شود.
</p>
<h2 className="text-xl font-medium tracking-tight md:text-3xl">
ما فقط یک شرکت فناوری یا فین‌تک معمولی نیستیم؛ رویکرد ما از پایه متفاوت است.
</h2>
<p className="text-lg">
مالکیت شرکت کاملاً در اختیار بنیان‌گذاران و اعضای تیم است. با حفظ تیمی چابک و سودآور، تمام تمرکز خود را بر ارائه زیرساختی قابل اعتماد برای توسعه‌دهندگان گذاشته‌ایم.
</p>
</div>
</section>
{/* Founding Team Section */}
<section className={cn("py-10 md:py-12 lg:py-15", contentShell)}>
<div className="grid gap-5 md:grid-cols-2 md:gap-10 lg:gap-16">
<div className="order-2 md:order-1">
<div className={textFromStart}>
<h2 className="text-xl font-semibold tracking-tight md:text-xl">
تیم بنیان‌گذار
</h2>
<p className="mt-5 text-lg md:mt-6">
ساخت ری یو آی را از ۲۱۹ آغاز کردیم و در ۲۲۲ عرضه کردیم. هر
نقطه اتصال از صفر طراحی شده بدون بدهی فنی یا سیستم کهنه. برای
نوآوری مالی در صد سال آینده ساخته شده‌ایم. صددرصد در مالکیت
بنیان‌گذاران و تیم است، سودآوریم و تیم را کوچک نگه می‌داریم.
به‌مرور این صفحه کامل‌تر می‌شود، اما الان تمرکزمان بر تحویل به
توسعه‌دهندگان است. اگر به ساخت آینده اتصال‌های مالی علاقه‌مندید،
موقعیت‌های باز زیر را ببینید.
</p>
</div>
</div>
<img
src="/block-media/shadcnblocks/block/placeholder-1.svg"
alt="همکاری تیم بنیان‌گذار"
width={480}
height={400}
className="order-1 object-cover md:order-2"
/>
</div>
</section>
</section>
);
};
interface PlusSignsProps extends SVGProps<SVGSVGElement> {
className?: string;
}
const PlusSigns = ({ className, ...props }: PlusSignsProps) => {
const GAP = 16;
const STROKE_WIDTH = 1;
const PLUS_SIZE = 6;
const id = useId();
const patternId = `plus-pattern-${id}`;
return (
<svg width={GAP * 2} height={GAP * 2} className={className} {...props}>
<defs>
<pattern
id={patternId}
x="0"
y="0"
width={GAP}
height={GAP}
patternUnits="userSpaceOnUse"
>
<line
x1={GAP / 2}
y1={(GAP - PLUS_SIZE) / 2}
x2={GAP / 2}
y2={(GAP + PLUS_SIZE) / 2}
stroke="currentColor"
strokeWidth={STROKE_WIDTH}
/>
<line
x1={(GAP - PLUS_SIZE) / 2}
y1={GAP / 2}
x2={(GAP + PLUS_SIZE) / 2}
y2={GAP / 2}
stroke="currentColor"
strokeWidth={STROKE_WIDTH}
/>
</pattern>
</defs>
<rect width="100%" height="100%" fill={`url(#${patternId})`} />
</svg>
);
};
export { About8 };
+64
View File
@@ -0,0 +1,64 @@
import React from "react";
import { cn } from "@/lib/utils";
interface About9Props {
className?: string;
}
const About9 = ({ className }: About9Props) => {
return (
<section className={cn("py-16 md:py-32", className)}>
<div className="container">
<div className="mx-auto max-w-6xl">
<h1 className="mb-6 max-w-4xl text-3xl leading-tight font-medium tracking-tight md:text-xl">
سلام، ما تیمی از توسعه‌دهندگان هستیم که با اشتیاق، اپلیکیشن‌های وب مقیاس‌پذیر، سریع و قابل اعتماد می‌سازیم.
</h1>
<div className="mb-8 flex justify-center">
<img
src="/block-media/shadcnblocks/block/photos/nubelson-fernandes-tAJYoec13xk-unsplash.jpg"
alt="تیم در حال کار"
className="w-full rounded-sm object-cover"
/>
</div>
<div className="mx-auto mb-16 flex w-full max-w-6xl flex-col justify-between gap-12 md:flex-row">
<div className="text-base font-medium">
<span className="block text-muted-foreground">
توسعه‌دهنده فول‌استک
</span>
<span className="font-semibold text-foreground">
San Francisco CA
<img
src="/block-media/shadcnblocks/block/flags/united-states.png"
alt="ایالات متحده"
className="h-4 w-4"
/>
</span>
</div>
<div className="mt-8 w-full md:mt-0 md:w-2/3">
<p className="font-base w-full text-lg leading-[28px] text-muted-foreground md:w-3/4">
من یک توسعه‌دهنده فول‌استک با بیش از ۶ سال تجربه در طراحی و توسعه اپلیکیشن‌های وب مقیاس‌پذیر هستم. علاقه به حل مسئله و فناوری، مسیر حرفه‌ای من را شکل داده است.
<br />
<br />
در رابط کاربری، سرور برنامه و فناوری‌های ابری تخصص دارم و به کسب‌وکار نوپا‌ها و سازمان‌ها کمک کرده‌ام محصولات دیجیتال مقیاس‌پذیر توسعه دهند. تمرکز من بر تجربه کاربری، معماری تمیز و کدی است که در بلندمدت به‌سادگی نگهداری شود.
</p>
<div className="mt-12">
<img
src="/block-media/shadcnblocks/block/photos/nastuh-abootalebi-eHD8Y1Znfpk-unsplash.jpg"
alt="محیط کار"
className="w-full rounded-sm object-cover"
/>
</div>
</div>
</div>
</div>
</div>
</section>
);
};
export { About9 };
+96
View File
@@ -0,0 +1,96 @@
import { FcGoogle } from "react-icons/fc";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
interface Hyperlink {
label: string;
href: string;
}
interface AcceptInvite1Props {
companyLogo?: string;
disclaimer?: string;
disclaimerLink?: Hyperlink;
heading?: string;
description?: string[];
copyright?: string;
footerLinks?: Hyperlink[];
}
const AcceptInvite1 = ({
companyLogo = "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo-word.svg",
disclaimer = "با وارد کردن ایمیل خود، با دریافت به‌روزرسانی‌ها و پیام‌های بازاریابی از آکمه موافقت می‌کنید. هر زمان می‌توانید اشتراک را لغو کنید. برای جزئیات بیشتر، ",
disclaimerLink = {
label: "سیاست حریم خصوصی",
href: "#",
},
heading = "به آکمه خوش آمدید",
description = [
"آکمه پلتفرمی نسل جدید است که برای ساده‌سازی گردش کار شما طراحی شده. با انعطاف‌پذیری در طراحی، با نیازهای منحصربه‌فرد کسب‌وکار شما سازگار می‌شود.",
"پروژه‌های خود را مدیریت کنید، با تیم همکاری کنید و پیشرفت را در یک داشبورد متمرکز دنبال کنید.",
"همین امروز شروع کنید.",
],
copyright = `© ${new Date().getFullYear().toLocaleString("fa-IR")} آکمه`,
footerLinks = [
{ label: "حریم خصوصی", href: "#" },
{
label: "پشتیبانی",
href: "#",
},
],
}: AcceptInvite1Props) => {
return (
<section dir="rtl" lang="fa" className="container py-32">
<div className="flex w-full flex-col gap-20 text-start sm:items-center">
<img
src={companyLogo}
alt="لوگوی شرکت"
className="w-54 dark:invert"
/>
<div className="flex flex-col-reverse items-start gap-10 sm:rounded-2xl sm:border sm:px-10 sm:pt-10 sm:pb-10 md:flex-row md:pt-20 lg:gap-20 lg:rounded-3xl lg:px-24 lg:pt-36 xl:gap-40">
<div className="flex w-full flex-col gap-6 sm:max-w-sm md:gap-60">
<div className="flex flex-col gap-6">
<Button type="button" variant="outline">
<FcGoogle />
ادامه با گوگل
</Button>
<span className="h-px w-full bg-border" />
<form autoComplete="off" className="flex flex-col gap-3">
<Input placeholder="ایمیل خود را وارد کنید" required />
<Button type="submit">ادامه</Button>
</form>
</div>
<p className="text-xs text-muted-foreground">
{disclaimer}
<a href={disclaimerLink.href} className="underline">
{disclaimerLink.label}
</a>
.
</p>
</div>
<div className="flex w-full flex-col gap-4 sm:max-w-sm">
<h3 className="text-xl font-bold">{heading}</h3>
<div className="space-y-4 text-sm font-medium text-muted-foreground">
{description.map((item, index) => (
<p key={index}>{item}</p>
))}
</div>
</div>
</div>
<div className="flex flex-wrap items-center gap-4 text-xs text-muted-foreground">
<p>{copyright}</p>
{footerLinks.map((link) => (
<a key={link.label} href={link.href} className="underline">
{link.label}
</a>
))}
</div>
</div>
</section>
);
};
export { AcceptInvite1 };
+55
View File
@@ -0,0 +1,55 @@
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface AcceptInvite2Props {
className?: string;
hostName?: string;
description?: string;
}
const AcceptInvite2 = ({
className,
hostName = "سارا احمدی",
description = "برای همکاری و دسترسی به منابعشان به فضای کار بپیوندید.",
}: AcceptInvite2Props) => {
return (
<section dir="rtl" lang="fa" className="py-32">
<div className="container">
<div
className={cn(
"mx-auto w-fit space-y-12 rounded-xl border px-6 pt-12 pb-10 shadow-lg",
className,
)}
>
<div className="mx-auto flex max-w-md flex-col items-center gap-2 text-center">
<span className="flex size-32 items-center justify-center rounded-full bg-muted text-2xll font-medium">
{hostName.charAt(0)}
</span>
<p className="mt-2 text-xl">
<span className="font-medium">{hostName}</span>
<span className="font-light">
{" "}
شما را به پیوستن به فضای کار دعوت کرده است.
</span>
</p>
<p className="max-w-sm text-muted-foreground">{description}</p>
</div>
<div className="space-y-3">
<div className="flex items-center justify-center gap-2">
<Button size="lg" variant="outline">
رد دعوت
</Button>
<Button size="lg">پذیرش دعوت</Button>
</div>
<p className="text-center text-xs text-muted-foreground">
اعتبار این دعوت‌نامه ۲۴ ساعت است
</p>
</div>
</div>
</div>
</section>
);
};
export { AcceptInvite2 };
+593
View File
@@ -0,0 +1,593 @@
"use client";
import {
MapPin,
MoreVertical,
Pencil,
Plus,
Save,
Trash2,
X,
} from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
interface Address {
id: string;
name: string;
street: string;
city: string;
state: string;
zip: string;
country: string;
phone: string;
isDefault: boolean;
type: "خانه" | "سرکار" | "سایر";
}
const DEFAULT_ADDRESSES: Address[] = [
{
id: "1",
name: "علی رضایی",
street: "123 Main Street, Apt 4B",
city: "New York",
state: "NY",
zip: "10001",
country: "ایران",
phone: "(212) 555-1234",
isDefault: true,
type: "خانه",
},
{
id: "2",
name: "علی رضایی",
street: "456 Business Ave, Suite 100",
city: "New York",
state: "NY",
zip: "10018",
country: "ایران",
phone: "(212) 555-5678",
isDefault: false,
type: "سرکار",
},
];
interface AddressBook1Props {
addresses?: Address[];
className?: string;
}
const AddressBook1 = ({
addresses: initialAddresses = DEFAULT_ADDRESSES,
className,
}: AddressBook1Props) => {
const [addresses, setAddresses] = useState(initialAddresses);
const [selectedId, setSelectedId] = useState(
addresses.find((a) => a.isDefault)?.id || addresses[0]?.id,
);
const [editingId, setEditingId] = useState<string | null>(null);
const [isAddingNew, setIsAddingNew] = useState(false);
const [editForm, setEditForm] = useState<Partial<Address>>({});
const startEditing = (address: Address) => {
setEditingId(address.id);
setEditForm({ ...address });
};
const cancelEditing = () => {
setEditingId(null);
setEditForm({});
};
const saveEdit = () => {
if (
editingId &&
editForm.name &&
editForm.street &&
editForm.city &&
editForm.state &&
editForm.zip &&
editForm.country &&
editForm.phone &&
editForm.type
) {
setAddresses((prev) =>
prev.map((addr) =>
addr.id === editingId
? ({
...addr,
...editForm,
} as Address)
: addr,
),
);
setEditingId(null);
setEditForm({});
}
};
const startAddingNew = () => {
setIsAddingNew(true);
setEditForm({
name: "",
street: "",
city: "",
state: "",
zip: "",
country: "ایران",
phone: "",
type: "خانه",
isDefault: false,
});
};
const cancelAddingNew = () => {
setIsAddingNew(false);
setEditForm({});
};
const saveNewAddress = () => {
if (
editForm.name &&
editForm.street &&
editForm.city &&
editForm.state &&
editForm.zip &&
editForm.country &&
editForm.phone &&
editForm.type
) {
const newAddress: Address = {
id: Date.now().toString(),
name: editForm.name,
street: editForm.street,
city: editForm.city,
state: editForm.state,
zip: editForm.zip,
country: editForm.country,
phone: editForm.phone,
type: editForm.type as "خانه" | "سرکار" | "سایر",
isDefault: addresses.length === 0,
};
setAddresses((prev) => [...prev, newAddress]);
setIsAddingNew(false);
setEditForm({});
if (addresses.length === 0) {
setSelectedId(newAddress.id);
}
}
};
const deleteAddress = (id: string) => {
setAddresses((prev) => prev.filter((addr) => addr.id !== id));
if (selectedId === id && addresses.length > 1) {
setSelectedId(addresses.find((a) => a.id !== id)?.id || "");
}
if (editingId === id) {
setEditingId(null);
setEditForm({});
}
};
return (
<section className={cn("w-full py-16 md:py-24 ", className)}>
<div className="w-full px-6 ">
{/* Header */}
<div className="mb-6 flex justify-between ">
<div>
<h1 className="text-xl font-semibold tracking-tight md:text-3xl">
آدرس‌های من
</h1>
<p className="mt-1 text-muted-foreground">
آدرس‌های ذخیره‌شده خود را مدیریت کنید.
</p>
</div>
<Button onClick={startAddingNew}>
<Plus className="mr-2 size-4" />
افزودن آدرس
</Button>
</div>
{/* Address List */}
<RadioGroup value={selectedId} onValueChange={setSelectedId}>
<div className="space-y-4 ">
{addresses.map((address) => (
<Card
key={address.id}
className={cn(
"cursor-pointer gap-0 p-0 transition-colors ",
selectedId === address.id && "border-primary",
editingId === address.id && "border-primary",
)}
onClick={() =>
editingId !== address.id && setSelectedId(address.id)
}
>
<CardContent className="p-4 ">
{editingId === address.id ? (
<div className="space-y-4">
<div className="flex items-center justify-between ">
<h3 className="font-semibold">ویرایش آدرس</h3>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
cancelEditing();
}}
>
<X className="size-4" />
</Button>
<Button
size="sm"
onClick={(e) => {
e.stopPropagation();
saveEdit();
}}
>
<Save className="mr-2 size-4" />
ذخیره
</Button>
</div>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor={`name-${address.id}`}>
نام کامل
</Label>
<Input
id={`name-${address.id}`}
value={editForm.name || ""}
onChange={(e) =>
setEditForm({ ...editForm, name: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`phone-${address.id}`}>تلفن</Label>
<Input
id={`phone-${address.id}`}
value={editForm.phone || ""}
onChange={(e) =>
setEditForm({
...editForm,
phone: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2 sm:col-span-2">
<Label htmlFor={`street-${address.id}`}>
آدرس خیابان
</Label>
<Input
id={`street-${address.id}`}
value={editForm.street || ""}
onChange={(e) =>
setEditForm({
...editForm,
street: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`city-${address.id}`}>شهر</Label>
<Input
id={`city-${address.id}`}
value={editForm.city || ""}
onChange={(e) =>
setEditForm({ ...editForm, city: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`state-${address.id}`}>وضعیت</Label>
<Input
id={`state-${address.id}`}
value={editForm.state || ""}
onChange={(e) =>
setEditForm({
...editForm,
state: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`zip-${address.id}`}>کد پستی</Label>
<Input
id={`zip-${address.id}`}
value={editForm.zip || ""}
onChange={(e) =>
setEditForm({ ...editForm, zip: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`country-${address.id}`}>
کشور
</Label>
<Input
id={`country-${address.id}`}
value={editForm.country || ""}
onChange={(e) =>
setEditForm({
...editForm,
country: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`type-${address.id}`}>نوع</Label>
<Select
value={editForm.type || "خانه"}
onValueChange={(value: "خانه" | "سرکار" | "سایر") =>
setEditForm({ ...editForm, type: value })
}
>
<SelectTrigger
id={`type-${address.id}`}
onClick={(e) => e.stopPropagation()}
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="خانه">خانه</SelectItem>
<SelectItem value="سرکار">سرکار</SelectItem>
<SelectItem value="سایر">سایر</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
) : (
<div className="flex gap-4 flex-row-reverse">
<RadioGroupItem
value={address.id}
id={address.id}
className="mt-1 "
/>
<div className="flex-1">
<div className="flex justify-between gap-2 flex-row-reverse">
<div>
<div className="flex items-center gap-2">
<span className="font-semibold">
{address.name}
</span>
{address.isDefault && (
<Badge
variant="secondary"
className="text-xs font-normal"
>
پیش‌فرض
</Badge>
)}
<Badge
variant="outline"
className="text-xs capitalize"
>
{address.type}
</Badge>
</div>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={(e) => e.stopPropagation()}
>
<MoreVertical className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
startEditing(address);
}}
>
<Pencil className="mr-2 size-4" />
ویرایش
</DropdownMenuItem>
<DropdownMenuItem
className="text-destructive"
onClick={(e) => {
e.stopPropagation();
deleteAddress(address.id);
}}
>
<Trash2 className="mr-2 size-4" />
حذف
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="mt-2 text-sm text-muted-foreground text-end">
<p>{address.street}</p>
<p>
{address.city}, {address.state} {address.zip}
</p>
<p>{address.country}</p>
<p className="mt-1">{address.phone}</p>
</div>
</div>
</div>
)}
</CardContent>
</Card>
))}
{isAddingNew && (
<Card className="gap-0 border-primary p-0">
<CardContent className="p-4">
<div className="space-y-4">
<div className="flex items-center justify-between flex-row-reverse">
<h3 className="font-semibold "> افزودن آدرس جدید</h3>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={cancelAddingNew}
>
<X className="size-4" />
</Button>
<Button size="sm" onClick={saveNewAddress}>
<Save className="mr-2 size-4" />
ذخیره
</Button>
</div>
</div>
<div className="grid gap-4 sm:grid-cols-2 ">
<div className="space-y-2">
<Label htmlFor="new-name">نام کامل</Label>
<Input
id="new-name"
value={editForm.name || ""}
onChange={(e) =>
setEditForm({ ...editForm, name: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-phone">تلفن</Label>
<Input
id="new-phone"
value={editForm.phone || ""}
onChange={(e) =>
setEditForm({ ...editForm, phone: e.target.value })
}
/>
</div>
<div className="space-y-2 sm:col-span-2">
<Label htmlFor="new-street">آدرس خیابان</Label>
<Input
id="new-street"
value={editForm.street || ""}
onChange={(e) =>
setEditForm({ ...editForm, street: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-city">شهر</Label>
<Input
id="new-city"
value={editForm.city || ""}
onChange={(e) =>
setEditForm({ ...editForm, city: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-state">شهرستان</Label>
<Input
id="new-state"
value={editForm.state || ""}
onChange={(e) =>
setEditForm({ ...editForm, state: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-zip">کد پستی</Label>
<Input
id="new-zip"
value={editForm.zip || ""}
onChange={(e) =>
setEditForm({ ...editForm, zip: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-country">کشور</Label>
<Input
id="new-country"
value={editForm.country || ""}
onChange={(e) =>
setEditForm({
...editForm,
country: e.target.value,
})
}
/>
</div>
<div className="space-y-2 sm:col-span-2">
<Label htmlFor="new-type">نوع</Label>
<Select
value={editForm.type || "خانه"}
onValueChange={(value: "خانه" | "سرکار" | "سایر") =>
setEditForm({ ...editForm, type: value })
}
>
<SelectTrigger id="new-type">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="خانه">خانه</SelectItem>
<SelectItem value="سرکار">سرکار</SelectItem>
<SelectItem value="سایر">سایر</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
</CardContent>
</Card>
)}
</div>
</RadioGroup>
{addresses.length === 0 && (
<Card className="p-0">
<CardContent className="flex flex-col items-center justify-center py-12">
<MapPin className="mb-4 size-12 text-muted-foreground" />
<h2 className="text-xl font-semibold">آدرسی ذخیره نشد</h2>
<p className="mt-2 text-muted-foreground">
برای سریع‌تر شدن فرایند خرید، یک آدرس جدید اضافه کنید.
</p>
<Button className="mt-4" onClick={startAddingNew}>
<Plus className="mr-2 size-4" />
اضافه کردن آدرس
</Button>
</CardContent>
</Card>
)}
</div>
</section>
);
};
export { AddressBook1 };
+563
View File
@@ -0,0 +1,563 @@
"use client";
import { Building, Check, Home, Plus, Save, Star, X } from "lucide-react";
import { useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";
interface Address {
id: string;
label: string;
name: string;
street: string;
city: string;
state: string;
zip: string;
country: string;
isDefault: boolean;
type: "home" | "work";
}
const DEFAULT_ADDRESSES: Address[] = [
{
id: "1",
label: "خانه",
name: "علی رضایی",
street: "123 Main Street, Apt 4B",
city: "New York",
state: "NY",
zip: "10001",
country: "United States",
isDefault: true,
type: "home",
},
{
id: "2",
label: "دفتر",
name: "علی رضایی",
street: "456 Business Ave, Suite 100",
city: "New York",
state: "NY",
zip: "10018",
country: "United States",
isDefault: false,
type: "work",
},
];
interface AddressBook2Props {
addresses?: Address[];
title?: string;
className?: string;
}
const AddressBook2 = ({
addresses: initialAddresses = DEFAULT_ADDRESSES,
title = "آدرس‌های ارسال",
className,
}: AddressBook2Props) => {
const [addresses, setAddresses] = useState(initialAddresses);
const [selectedId, setSelectedId] = useState(
addresses.find((a) => a.isDefault)?.id || addresses[0]?.id,
);
const [editingId, setEditingId] = useState<string | null>(null);
const [isAddingNew, setIsAddingNew] = useState(false);
const [editForm, setEditForm] = useState<Partial<Address>>({});
const typeIcons = {
home: <Home className="size-4" />,
work: <Building className="size-4" />,
};
const startEditing = (address: Address) => {
setEditingId(address.id);
setEditForm({ ...address });
};
const cancelEditing = () => {
setEditingId(null);
setEditForm({});
};
const saveEdit = () => {
if (
editingId &&
editForm.name &&
editForm.street &&
editForm.city &&
editForm.state &&
editForm.zip &&
editForm.country &&
editForm.type &&
editForm.label
) {
setAddresses((prev) =>
prev.map((addr) =>
addr.id === editingId
? ({
...addr,
...editForm,
} as Address)
: addr,
),
);
setEditingId(null);
setEditForm({});
}
};
const startAddingNew = () => {
setIsAddingNew(true);
setEditForm({
label: "",
name: "",
street: "",
city: "",
state: "",
zip: "",
country: "ایران",
type: "home",
isDefault: addresses.length === 0,
});
};
const cancelAddingNew = () => {
setIsAddingNew(false);
setEditForm({});
};
const saveNewAddress = () => {
if (
editForm.name &&
editForm.street &&
editForm.city &&
editForm.state &&
editForm.zip &&
editForm.country &&
editForm.type &&
editForm.label
) {
const newAddress: Address = {
id: Date.now().toString(),
label: editForm.label,
name: editForm.name,
street: editForm.street,
city: editForm.city,
state: editForm.state,
zip: editForm.zip,
country: editForm.country,
type: editForm.type as "home" | "work",
isDefault: addresses.length === 0,
};
setAddresses((prev) => [...prev, newAddress]);
setIsAddingNew(false);
setEditForm({});
if (addresses.length === 0) {
setSelectedId(newAddress.id);
}
}
};
return (
<section className={cn("py-16 md:py-24", className)}>
<div className="container max-w-2xl">
{/* Header */}
<div className="mb-6">
<h2 className="text-xl font-semibold">{title}</h2>
</div>
{/* Address Grid */}
<div className="grid gap-4 sm:grid-cols-2">
{addresses.map((address) => (
<Card
key={address.id}
className={cn(
"relative cursor-pointer gap-0 border p-0 transition-all hover:shadow-md",
selectedId === address.id && editingId !== address.id
? "border-primary shadow-md ring-2 ring-primary"
: "",
editingId === address.id && "border-muted",
)}
onClick={() =>
editingId !== address.id && setSelectedId(address.id)
}
>
<CardContent className="p-4">
{editingId === address.id ? (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="font-semibold">ویرایش آدرس</h3>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.stopPropagation();
cancelEditing();
}}
>
<X className="size-4" />
</Button>
<Button
size="sm"
onClick={(e) => {
e.stopPropagation();
saveEdit();
}}
>
<Save className="mr-2 size-4" />
ذخیره
</Button>
</div>
</div>
<div className="grid gap-4">
<div className="space-y-2">
<Label htmlFor={`label-${address.id}`}>عنوان</Label>
<Input
id={`label-${address.id}`}
value={editForm.label || ""}
onChange={(e) =>
setEditForm({ ...editForm, label: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`name-${address.id}`}>نام کامل </Label>
<Input
id={`name-${address.id}`}
value={editForm.name || ""}
onChange={(e) =>
setEditForm({ ...editForm, name: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`street-${address.id}`}>
آدرس خیابان
</Label>
<Input
id={`street-${address.id}`}
value={editForm.street || ""}
onChange={(e) =>
setEditForm({ ...editForm, street: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-2">
<Label htmlFor={`city-${address.id}`}>شهر</Label>
<Input
id={`city-${address.id}`}
value={editForm.city || ""}
onChange={(e) =>
setEditForm({ ...editForm, city: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`state-${address.id}`}>وضعیت</Label>
<Input
id={`state-${address.id}`}
value={editForm.state || ""}
onChange={(e) =>
setEditForm({
...editForm,
state: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-2">
<Label htmlFor={`zip-${address.id}`}>کد پستی </Label>
<Input
id={`zip-${address.id}`}
value={editForm.zip || ""}
onChange={(e) =>
setEditForm({ ...editForm, zip: e.target.value })
}
onClick={(e) => e.stopPropagation()}
/>
</div>
<div className="space-y-2">
<Label htmlFor={`country-${address.id}`}>
کشور
</Label>
<Input
id={`country-${address.id}`}
value={editForm.country || ""}
onChange={(e) =>
setEditForm({
...editForm,
country: e.target.value,
})
}
onClick={(e) => e.stopPropagation()}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor={`type-${address.id}`}>نوع</Label>
<Select
value={editForm.type || "home"}
onValueChange={(value: "home" | "work") =>
setEditForm({ ...editForm, type: value })
}
>
<SelectTrigger
id={`type-${address.id}`}
onClick={(e) => e.stopPropagation()}
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="home">خانه</SelectItem>
<SelectItem value="work">محل کار</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
) : (
<>
{/* Selected Indicator */}
{selectedId === address.id && (
<div className="absolute top-3 right-3 flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground">
<Check className="size-4" />
</div>
)}
{/* Type Icon & Label */}
<div className="mb-3 flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-full bg-muted">
{typeIcons[address.type]}
</div>
<span className="font-medium">{address.label}</span>
{address.isDefault && (
<Badge variant="secondary" className="gap-1 text-xs">
<Star className="size-3 fill-current" />
پیش‌فرض
</Badge>
)}
</div>
{/* Address Details */}
<div className="text-sm text-muted-foreground">
<p className="font-medium text-foreground">
{address.name}
</p>
<p>{address.street}</p>
<p>
{address.city}, {address.state} {address.zip}
</p>
</div>
{/* Actions */}
<div className="mt-4 flex gap-2">
<Button
variant="ghost"
size="sm"
className="h-8 px-2 text-xs"
onClick={(e) => {
e.stopPropagation();
startEditing(address);
}}
>
ویرایش
</Button>
{!address.isDefault && (
<Button
variant="ghost"
size="sm"
className="h-8 px-2 text-xs text-destructive hover:text-destructive"
onClick={(e) => {
e.stopPropagation();
setAddresses((prev) =>
prev.filter((a) => a.id !== address.id),
);
if (
selectedId === address.id &&
addresses.length > 1
) {
setSelectedId(
addresses.find((a) => a.id !== address.id)
?.id || "",
);
}
if (editingId === address.id) {
setEditingId(null);
setEditForm({});
}
}}
>
حذف
</Button>
)}
</div>
</>
)}
</CardContent>
</Card>
))}
{/* افزودن جدید Card */}
{isAddingNew ? (
<Card className="gap-0 border border-muted p-0">
<CardContent className="p-4">
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="font-semibold">افزودن آدرس جدید</h3>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={cancelAddingNew}
>
<X className="size-4" />
</Button>
<Button size="sm" onClick={saveNewAddress}>
<Save className="mr-2 size-4" />
ذخیره
</Button>
</div>
</div>
<div className="grid gap-4">
<div className="space-y-2">
<Label htmlFor="new-label">عنوان</Label>
<Input
id="new-label"
value={editForm.label || ""}
onChange={(e) =>
setEditForm({ ...editForm, label: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-name">نام کامل</Label>
<Input
id="new-name"
value={editForm.name || ""}
onChange={(e) =>
setEditForm({ ...editForm, name: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-street">آدرس خیابان</Label>
<Input
id="new-street"
value={editForm.street || ""}
onChange={(e) =>
setEditForm({ ...editForm, street: e.target.value })
}
/>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-2">
<Label htmlFor="new-city">شهر</Label>
<Input
id="new-city"
value={editForm.city || ""}
onChange={(e) =>
setEditForm({ ...editForm, city: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-state">وضعیت</Label>
<Input
id="new-state"
value={editForm.state || ""}
onChange={(e) =>
setEditForm({ ...editForm, state: e.target.value })
}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<div className="space-y-2">
<Label htmlFor="new-zip">کد پستی</Label>
<Input
id="new-zip"
value={editForm.zip || ""}
onChange={(e) =>
setEditForm({ ...editForm, zip: e.target.value })
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-country">کشور</Label>
<Input
id="new-country"
value={editForm.country || ""}
onChange={(e) =>
setEditForm({
...editForm,
country: e.target.value,
})
}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="new-type">نوع</Label>
<Select
value={editForm.type || "home"}
onValueChange={(value: "home" | "work") =>
setEditForm({ ...editForm, type: value })
}
>
<SelectTrigger id="new-type">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="home">خانه</SelectItem>
<SelectItem value="work">محل کار</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
</CardContent>
</Card>
) : (
<Card
className="cursor-pointer gap-0 border-dashed p-0 transition-colors hover:border-primary hover:bg-muted/50"
onClick={startAddingNew}
>
<CardContent className="flex h-full min-h-[180px] flex-col items-center justify-center p-4 text-center">
<div className="mb-3 flex size-12 items-center justify-center rounded-full bg-muted">
<Plus className="size-5 text-muted-foreground" />
</div>
<p className="font-medium">افزودن آدرس جدید</p>
<p className="mt-1 text-sm text-muted-foreground">
آدرس خود را برای ارسال سفارش‌ها ذخیره کنید.
</p>
</CardContent>
</Card>
)}
</div>
</div>
</section>
);
};
export { AddressBook2 };
+441
View File
@@ -0,0 +1,441 @@
"use client";
import {
BadgeCheck,
BarChart3,
Briefcase,
ChevronRight,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
HelpCircle,
LayoutDashboard,
LogOut,
Settings,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
// Base nav item - used by simple sidebars
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
// Optional children for submenus (Sidebar3+)
children?: NavItem[];
};
// Nav group with optional collapsible state
type NavGroup = {
title: string;
items: NavItem[];
// Optional: default collapsed state (Sidebar2+)
defaultOpen?: boolean;
};
// User data for footer (Sidebar6+)
type UserData = {
name: string;
email: string;
avatar: string;
};
// Complete sidebar data structure
type SidebarData = {
// Logo/branding (all sidebars)
logo: {
src: string;
alt: string;
title: string;
description: string;
};
// Main navigation groups (all sidebars)
navGroups: NavGroup[];
// Footer navigation group (all sidebars)
footerGroup: NavGroup;
// User data for user footer (Sidebar6+)
user?: UserData;
// Workspaces for switcher (Sidebar7+)
workspaces?: Array<{
id: string;
name: string;
logo: string;
plan: string;
}>;
// Currently active workspace (Sidebar7+)
activeWorkspace?: string;
};
// Shared sidebar data - works with all sidebar variations
const sidebarData: SidebarData = {
logo: {
src: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
alt: "شاد‌سی‌ان بلاکس",
title: "شاد‌سی‌ان بلاکس",
description: "اپلیکیشن خود را بسازید",
},
navGroups: [
{
title: "نمای کلی",
defaultOpen: true,
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
defaultOpen: true,
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۲۲۴", icon: FileText, href: "#" },
{ label: "بایگانی ۲۲۳", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
defaultOpen: false,
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
defaultOpen: false,
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
],
footerGroup: {
title: "پشتیبانی",
items: [
{ label: "راهنمای کاربران", icon: HelpCircle, href: "#" },
{ label: "تنظیمات", icon: Settings, href: "#" },
],
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-2.webp",
},
workspaces: [
{
id: "1",
name: "شاد‌سی‌ان بلاکس",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "سازمانی",
},
{
id: "2",
name: "قالب‌های شاد‌سی‌ان",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "استارتاپ",
},
{
id: "3",
name: "کامپوننت‌های شاد‌سی‌ان",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "رایگان",
},
],
activeWorkspace: "1",
};
const getInitials = (name: string) =>
name
.split(" ")
.map((part) => part[0])
.join("");
const SidebarLogo = ({ logo }: { logo: SidebarData["logo"] }) => {
return (
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={logo.src}
alt={logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<div className="flex flex-col gap-0.5 text-start leading-none">
<span className="font-medium">{logo.title}</span>
<span className="text-xs text-muted-foreground">
{logo.description}
</span>
</div>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
);
};
const NavMenuItem = ({ item }: { item: NavItem }) => {
const Icon = item.icon;
const hasChildren = item.children && item.children.length > 0;
if (!hasChildren) {
return (
<SidebarMenuItem>
<SidebarMenuButton
asChild
isActive={item.isActive}
tooltip={item.label}
>
<a href={item.href} className="text-start">
<Icon className="size-4" aria-hidden="true" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
}
return (
<Collapsible asChild defaultOpen className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton isActive={item.isActive} tooltip={item.label}>
<Icon className="size-4" aria-hidden="true" />
<span>{item.label}</span>
<ChevronRight
className="ms-auto size-4 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180"
aria-hidden="true"
/>
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.children!.map((child) => (
<SidebarMenuSubItem key={child.label}>
<SidebarMenuSubButton asChild isActive={child.isActive}>
<a href={child.href} className="text-start">
{child.label}
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
);
};
const NavUser = ({ user }: { user: UserData }) => {
const initials = getInitials(user.name);
const userAvatar = (
<Avatar className="size-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">{initials}</AvatarFallback>
</Avatar>
);
const userInfo = (
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">
{user.email}
</span>
</div>
);
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
{userAvatar}
{userInfo}
<ChevronsUpDown className="ms-auto size-4" aria-hidden="true" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side="bottom"
align="start"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
{userAvatar}
{userInfo}
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" aria-hidden="true" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" aria-hidden="true" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
};
const AppSidebar = ({ ...props }: React.ComponentProps<typeof Sidebar>) => {
return (
<Sidebar side="right" {...props}>
<SidebarHeader>
<SidebarLogo logo={sidebarData.logo} />
</SidebarHeader>
<SidebarContent className="overflow-hidden">
<ScrollArea className="min-h-0 flex-1">
{sidebarData.navGroups.map((group) => (
<SidebarGroup key={group.title}>
<SidebarGroupLabel>{group.title}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{group.items.map((item) => (
<NavMenuItem key={item.label} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
))}
</ScrollArea>
</SidebarContent>
<SidebarFooter>
<SidebarGroup>
<SidebarGroupLabel>{sidebarData.footerGroup.title}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{sidebarData.footerGroup.items.map((item) => (
<NavMenuItem key={item.label} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
{sidebarData.user && <NavUser user={sidebarData.user} />}
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
};
interface ApplicationShell1Props {
className?: string;
}
export function ApplicationShell1({ className }: ApplicationShell1Props) {
return (
<SidebarProvider dir="rtl" lang="fa" className={cn(className)}>
<AppSidebar />
<SidebarInset dir="rtl" lang="fa">
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ms-1" aria-label="باز و بسته کردن منو" />
<Separator
orientation="vertical"
className="ms-2 data-[orientation=vertical]:h-4"
/>
<Breadcrumb>
<BreadcrumbList className="text-start">
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">نمای کلی</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block [&>svg]:راست‌به‌چپ:rotate-180" />
<BreadcrumbItem>
<BreadcrumbPage>داشبورد</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</header>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</div>
</SidebarInset>
</SidebarProvider>
);
}
File diff suppressed because it is too large Load Diff
+672
View File
@@ -0,0 +1,672 @@
"use client";
import {
Baby,
BadgeCheck,
Bell,
ChevronDown,
ChevronRight,
Clapperboard,
Clock,
CreditCard,
Download,
Film,
Flag,
HelpCircle,
History,
Home,
ListVideo,
LogOut,
type LucideIcon,
Menu,
MessageSquare,
Music,
Music2,
PlaySquare,
Radio,
Search,
Settings,
ShoppingBag,
Sparkles,
ThumbsUp,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Kbd } from "@/components/ui/kbd";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
useSidebar,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
function getInitials(name: string) {
return (
name
.split(" ")
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]?.toUpperCase())
.join("") || "U"
);
}
const data = {
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar/avatar1.jpg",
},
navPrimary: [
{ title: "خانه", url: "#", icon: Home, isActive: true },
{ title: "ویدیوهای کوتاه", url: "#", icon: Clapperboard },
{ title: "اشتراک‌ها", url: "#", icon: PlaySquare },
{ title: "شما", url: "#", icon: History },
],
subscriptions: [
{ name: "Theo - t3.gg", avatar: "/avatars/theo.jpg", isLive: true },
{ name: "Fireship", avatar: "/avatars/fireship.jpg", hasNew: true },
{ name: "Web Dev Simplified", avatar: "/avatars/webdev.jpg" },
{ name: "Traversy Media", avatar: "/avatars/traversy.jpg", hasNew: true },
{ name: "The Primeagen", avatar: "/avatars/prime.jpg" },
{ name: "Jack Herrington", avatar: "/avatars/jack.jpg", hasNew: true },
{ name: "Coding in Public", avatar: "/avatars/cip.jpg" },
],
navYou: [
{ title: "تاریخچه", url: "#", icon: History },
{ title: "لیست‌های پخش", url: "#", icon: ListVideo },
{ title: "تماشا بعداً", url: "#", icon: Clock },
{ title: "ویدیوهای پسندیده", url: "#", icon: ThumbsUp },
{ title: "ویدیوهای شما", url: "#", icon: PlaySquare },
{ title: "دانلودها", url: "#", icon: Download },
],
navExplore: [
{ title: "خرید", url: "#", icon: ShoppingBag },
{ title: "موسیقی", url: "#", icon: Music },
{ title: "فیلم", url: "#", icon: Film },
],
navMoreFrom: [
{ title: "استودیوی ری یو آی", url: "#", icon: Radio },
{ title: "موسیقی ری یو آی", url: "#", icon: Music2 },
{ title: "ری یو آی کودکان", url: "#", icon: Baby },
],
navFooter: [
{ title: "تنظیمات", url: "#", icon: Settings },
{ title: "تاریخچه گزارش", url: "#", icon: Flag },
{ title: "راهنما", url: "#", icon: HelpCircle },
{ title: "ارسال بازخورد", url: "#", icon: MessageSquare },
],
};
// Primary nav (Home, Shorts, Subscriptions, You)
function NavPrimary({
items,
}: {
items: { title: string; url: string; icon: LucideIcon; isActive?: boolean }[];
}) {
return (
<SidebarGroup>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton
asChild
isActive={item.isActive}
tooltip={item.title}
>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
// Subscriptions section with channel avatars
function NavSubscriptions({
subscriptions,
}: {
subscriptions: {
name: string;
avatar: string;
isLive?: boolean;
hasNew?: boolean;
}[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<Collapsible defaultOpen>
<SidebarGroupLabel asChild>
<CollapsibleTrigger className="group/collapsible flex w-full items-center text-start">
اشتراک‌ها
<ChevronRight className="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarMenu>
{subscriptions.map((channel) => (
<SidebarMenuItem key={channel.name}>
<SidebarMenuButton asChild>
<a href="#">
<Avatar className="size-6 ring-2 ring-muted">
<AvatarImage src={channel.avatar} />
<AvatarFallback className="bg-primary text-xs text-primary-foreground">
{channel.name[0]}
</AvatarFallback>
</Avatar>
<span className="truncate">{channel.name}</span>
{channel.isLive && (
<Radio className="ms-auto size-4 text-red-500" />
)}
{channel.hasNew && !channel.isLive && (
<span className="ms-auto size-2 rounded-full bg-blue-500" />
)}
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
<SidebarMenuItem>
<SidebarMenuButton>
<ChevronDown className="size-4" />
<span>نمایش بیشتر</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</CollapsibleContent>
</Collapsible>
</SidebarGroup>
);
}
// "You" section - History, Playlists, etc.
function NavYou({
items,
}: {
items: { title: string; url: string; icon: LucideIcon }[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<Collapsible defaultOpen>
<SidebarGroupLabel asChild>
<CollapsibleTrigger className="group/collapsible flex w-full items-center text-start">
شما
<ChevronRight className="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
<SidebarMenuItem>
<SidebarMenuButton>
<ChevronDown className="size-4" />
<span>نمایش بیشتر</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</CollapsibleContent>
</Collapsible>
</SidebarGroup>
);
}
// Explore section - Shopping, Music, Movies
function NavExplore({
items,
}: {
items: { title: string; url: string; icon: LucideIcon }[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<Collapsible defaultOpen>
<SidebarGroupLabel asChild>
<CollapsibleTrigger className="group/collapsible flex w-full items-center text-start">
کاوش
<ChevronRight className="ms-auto transition-transform group-data-[state=open]/collapsible:rotate-90 rtl:rotate-180" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
<SidebarMenuItem>
<SidebarMenuButton>
<ChevronDown className="size-4" />
<span>نمایش بیشتر</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</CollapsibleContent>
</Collapsible>
</SidebarGroup>
);
}
// More from Zerotube section
function NavMoreFrom({
items,
}: {
items: { title: string; url: string; icon: LucideIcon }[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>بیشتر از ری یو آی</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
// Footer nav - Settings, Help, etc.
function NavFooter({
items,
}: {
items: { title: string; url: string; icon: LucideIcon }[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild size="sm">
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
// Footer links at the very bottom
function FooterLinks() {
return (
<div className="px-4 py-4 text-start text-xs text-muted-foreground">
<div className="flex flex-wrap gap-x-2 gap-y-1">
<a href="#" className="hover:underline">
درباره
</a>
<a href="#" className="hover:underline">
مطبوعات
</a>
<a href="#" className="hover:underline">
حق نشر
</a>
<a href="#" className="hover:underline">
تماس با ما
</a>
<a href="#" className="hover:underline">
سازندگان
</a>
<a href="#" className="hover:underline">
تبلیغات
</a>
<a href="#" className="hover:underline">
توسعه‌دهندگان
</a>
</div>
<div className="mt-2 flex flex-wrap gap-x-2 gap-y-1">
<a href="#" className="hover:underline">
قوانین
</a>
<a href="#" className="hover:underline">
حریم خصوصی
</a>
<a href="#" className="hover:underline">
سیاست و ایمنی
</a>
</div>
<p className="mt-3">© ۱۴۰۴ ری یو آی</p>
</div>
);
}
function SiteHeader() {
const { toggleSidebar } = useSidebar();
return (
<header className="fixed top-0 z-50 hidden w-full items-center border-b bg-background md:flex">
{/* Left: Menu + Logo */}
<div className="flex h-(--header-height) w-(--sidebar-width-icon) shrink-0 items-center justify-center">
<Button
className="size-9"
variant="ghost"
size="icon"
onClick={toggleSidebar}
aria-label="منو"
>
<Menu className="size-5" />
</Button>
</div>
<div className="flex h-(--header-height) items-center pe-4">
<a href="#" className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-5 invert dark:invert-0"
/>
</div>
<span className="hidden text-lg font-semibold sm:block">
ری یو آی
</span>
</a>
</div>
{/* Center: Search Bar */}
<div className="flex flex-1 justify-center px-4">
<InputGroup className="h-10 max-w-xl rounded-full">
<InputGroupAddon>
<Search className="text-muted-foreground" />
</InputGroupAddon>
<InputGroupInput placeholder="جستجو" />
<InputGroupAddon align="inline-end">
<Kbd>⌘K</Kbd>
</InputGroupAddon>
</InputGroup>
</div>
{/* Right: Avatar */}
<div className="flex h-(--header-height) items-center gap-1 px-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="flex h-auto items-center gap-2 px-2 py-1"
>
<Avatar className="size-8">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback>{getInitials(data.user.name)}</AvatarFallback>
</Avatar>
<span className="hidden font-medium sm:inline">
{data.user.name}
</span>
<ChevronDown className="size-3 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="start">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1 text-start">
<p className="text-sm font-medium">{data.user.name}</p>
<p className="text-xs text-muted-foreground">
{data.user.email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles className="me-2 size-4" />
ارتقا به نسخه حرفه‌ای
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard className="me-2 size-4" />
صورتحساب
</DropdownMenuItem>
<DropdownMenuItem>
<Bell className="me-2 size-4" />
اعلان‌ها
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
);
}
function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar
side="right"
collapsible="icon"
className="top-(--header-height) hidden h-[calc(100svh-var(--header-height))]! md:flex"
{...props}
>
<SidebarContent className="overflow-hidden">
<ScrollArea className="min-h-0 flex-1">
<NavPrimary items={data.navPrimary} />
<NavSubscriptions subscriptions={data.subscriptions} />
<NavYou items={data.navYou} />
<NavExplore items={data.navExplore} />
<NavMoreFrom items={data.navMoreFrom} />
<NavFooter items={data.navFooter} />
</ScrollArea>
</SidebarContent>
<SidebarFooter className="group-data-[collapsible=icon]:hidden">
<FooterLinks />
</SidebarFooter>
</Sidebar>
);
}
export const iframeHeight = "800px";
export const description = "سایدبار با هدر و فرم جستجو.";
function MobileHeader() {
return (
<header className="sticky top-0 z-50 flex h-14 items-center justify-between border-b bg-background px-4 md:hidden">
<a href="#" className="flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-5 invert dark:invert-0"
/>
</div>
<span className="text-lg font-semibold">ری یو آی</span>
</a>
<div className="flex items-center gap-1">
<Button variant="ghost" size="icon" className="size-9" aria-label="جستجو">
<Search className="size-5" />
<span className="sr-only">جستجو</span>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="size-9">
<Avatar className="size-8">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback>{getInitials(data.user.name)}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="start">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1 text-start">
<p className="text-sm font-medium">{data.user.name}</p>
<p className="text-xs text-muted-foreground">
{data.user.email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles className="me-2 size-4" />
ارتقا به نسخه حرفه‌ای
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard className="me-2 size-4" />
صورتحساب
</DropdownMenuItem>
<DropdownMenuItem>
<Bell className="me-2 size-4" />
اعلان‌ها
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
);
}
function MobileBottomNav() {
return (
<nav className="fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 backdrop-blur md:hidden">
<div className="grid grid-cols-4">
{data.navPrimary.map((item) => {
const Icon = item.icon;
return (
<button
key={item.title}
type="button"
className={cn(
"flex flex-col items-center gap-1 py-2 text-xs transition-colors",
item.isActive
? "text-foreground"
: "text-muted-foreground hover:text-foreground",
)}
>
<Icon className="size-5" />
<span className="sr-only">{item.title}</span>
<span aria-hidden="true">{item.title}</span>
</button>
);
})}
</div>
</nav>
);
}
function ContentGrid() {
return (
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
</div>
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
</div>
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
</div>
</div>
);
}
export function ApplicationShell11() {
return (
<div className="w-full [--header-height:calc(--spacing(14))]">
<SidebarProvider
dir="rtl"
lang="fa"
className="flex flex-col"
style={{ "--sidebar-width-icon": "3rem" } as React.CSSProperties}
>
{/* Desktop layout */}
<SiteHeader />
<div className="hidden flex-1 pt-(--header-height) md:flex">
<AppSidebar />
<SidebarInset>
<ContentGrid />
</SidebarInset>
</div>
{/* Mobile layout */}
<div className="flex flex-col md:hidden">
<MobileHeader />
<div className="pb-16">
<ContentGrid />
</div>
<MobileBottomNav />
</div>
</SidebarProvider>
</div>
);
}
File diff suppressed because it is too large Load Diff
+454
View File
@@ -0,0 +1,454 @@
"use client";
import {
BadgeCheck,
BarChart3,
Bell,
Briefcase,
ChevronDown,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
LayoutDashboard,
LogOut,
Menu,
Search,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import type { ComponentType, SVGProps } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
type NavItem = {
label: string;
icon: ComponentType<SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
children?: NavItem[];
};
type NavGroup = {
title: string;
items: NavItem[];
};
const data = {
brand: {
name: "ShadcnBlocks",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-1.webp",
},
organization: {
name: "شرکت آکمه",
logo: "/block-media/shadcnblocks/block/logos/vercel-icon.svg",
},
};
const navGroups: NavGroup[] = [
{
title: "نمای کلی",
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۱۴۰۳", icon: FileText, href: "#" },
{ label: "بایگانی ۱۴۲", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
];
function getInitials(name: string) {
return name
.split(" ")
.map((part) => part[0])
.join("")
.slice(0, 2)
.toUpperCase();
}
function OrganizationSwitcher() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-2 px-2">
<span className="flex size-6 shrink-0 items-center justify-center rounded bg-primary">
<img
src={data.organization.logo}
alt={data.organization.name}
width={16}
height={16}
className="size-4 invert dark:invert-0"
/>
</span>
<span className="truncate text-sm font-medium text-foreground">
{data.organization.name}
</span>
<ChevronDown className="ms-auto size-4 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel>سازمان‌ها</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<div className="flex items-center gap-2">
<span className="flex size-6 items-center justify-center rounded bg-primary">
<img
src={data.organization.logo}
alt={data.organization.name}
width={16}
height={16}
className="size-4 invert dark:invert-0"
/>
</span>
<span>{data.organization.name}</span>
</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>ایجاد سازمان</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
const NavDropdown = ({ group }: { group: NavGroup }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-1">
{group.title}
<ChevronDown className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48">
{group.items.map((item) => {
const Icon = item.icon;
return (
<div key={item.label}>
<DropdownMenuItem asChild>
<a href={item.href} className="flex items-center gap-2">
<Icon className="size-4" />
{item.label}
</a>
</DropdownMenuItem>
{item.children?.map((child) => {
const ChildIcon = child.icon;
return (
<DropdownMenuItem key={child.label} asChild className="ps-6">
<a href={child.href} className="flex items-center gap-2">
<ChildIcon className="size-4" />
{child.label}
</a>
</DropdownMenuItem>
);
})}
</div>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
};
function MobileNav() {
return (
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="باز کردن منو"
>
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" dir="rtl" className="w-72 p-0 text-start">
<SheetHeader className="px-4 pt-4">
<SheetTitle className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={data.brand.logo}
alt="شاد‌سی‌ان بلاکس"
width={20}
height={20}
className="size-5 invert dark:invert-0"
/>
</div>
{data.brand.name}
</SheetTitle>
</SheetHeader>
<ScrollArea className="min-h-0 flex-1">
<nav className="flex flex-col gap-4 px-4 py-4">
{navGroups.map((group) => (
<div key={group.title}>
<div className="mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase">
{group.title}
</div>
<div className="flex flex-col gap-1">
{group.items.map((item) => {
const Icon = item.icon;
return (
<div key={item.label}>
<a
href={item.href}
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted",
item.isActive && "bg-muted font-medium",
)}
>
<Icon className="size-4" />
{item.label}
</a>
{item.children?.map((child) => {
const ChildIcon = child.icon;
return (
<a
key={child.label}
href={child.href}
className="flex items-center gap-2 rounded-md py-1.5 pe-2 ps-8 text-sm hover:bg-muted"
>
<ChildIcon className="size-4" />
{child.label}
</a>
);
})}
</div>
);
})}
</div>
</div>
))}
</nav>
</ScrollArea>
</SheetContent>
</Sheet>
);
}
function SearchField({ className }: { className?: string }) {
return (
<div className={cn("relative", className)}>
<Search className="pointer-events-none absolute top-1/2 start-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
type="search"
placeholder="جستجو..."
className="h-9 w-full ps-8"
aria-label="جستجو"
/>
</div>
);
}
function NavUser() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-2 px-2">
<Avatar className="size-8">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback className="text-xs">
{getInitials(data.user.name)}
</AvatarFallback>
</Avatar>
<span className="hidden text-sm font-medium md:inline">
{data.user.name}
</span>
<ChevronsUpDown className="hidden size-4 md:block" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1 text-start">
<p className="text-sm font-medium">{data.user.name}</p>
<p className="text-xs text-muted-foreground">{data.user.email}</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
interface ApplicationShell13Props {
className?: string;
}
export function ApplicationShell13({ className }: ApplicationShell13Props) {
return (
<div
dir="rtl"
lang="fa"
className={cn("flex min-h-svh flex-col bg-muted/50", className)}
>
<header className="sticky top-0 z-40 w-full border-b border-border bg-background">
<div className="mx-auto grid h-14 w-full grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-4 lg:px-6">
<div className="flex items-center gap-3">
<MobileNav />
<a href="#" className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={data.brand.logo}
alt="شاد‌سی‌ان بلاکس"
width={20}
height={20}
className="size-5 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{data.brand.name}</span>
</a>
</div>
<div className="hidden w-full justify-self-center md:block md:max-w-md">
<SearchField />
</div>
<div className="flex items-center gap-2 justify-self-end">
<Button variant="ghost" size="icon" aria-label="اعلان‌ها">
<Bell className="size-4" />
</Button>
<NavUser />
</div>
</div>
<div className="border-t border-border md:hidden">
<div className="px-4 py-3">
<SearchField />
</div>
</div>
<div className="hidden border-t border-border md:block">
<div className="mx-auto flex items-center gap-3 overflow-x-auto px-4 py-2 lg:px-6">
<nav className="flex items-center gap-1">
{navGroups.map((group) => (
<NavDropdown key={group.title} group={group} />
))}
</nav>
<div className="ms-auto max-w-52">
<OrganizationSwitcher />
</div>
</div>
</div>
</header>
<main className="flex-1 px-4 py-6 pb-24 md:pb-6 lg:px-6">
<div className="mx-auto max-w-5xl">
<div className="min-h-[360px]" />
</div>
</main>
{/* Mobile Bottom Navigation */}
<BottomNav />
</div>
);
}
const bottomNavItems = [
{ label: "نمای کلی", icon: LayoutDashboard, href: "#", isActive: true },
{ label: "پروژه‌ها", icon: Briefcase, href: "#" },
{ label: "تیم", icon: Users, href: "#" },
{ label: "فضای کار", icon: Globe2, href: "#" },
];
function BottomNav() {
return (
<nav className="fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/95 backdrop-blur md:hidden">
<div className="grid grid-cols-4">
{bottomNavItems.map((item) => {
const Icon = item.icon;
return (
<a
key={item.label}
href={item.href}
className={cn(
"flex flex-col items-center gap-1 py-2 text-xs",
item.isActive
? "text-primary"
: "text-muted-foreground hover:text-foreground",
)}
>
<Icon className="size-5" aria-hidden="true" />
<span className="sr-only">{item.label}</span>
<span aria-hidden="true">{item.label}</span>
</a>
);
})}
</div>
</nav>
);
}
+610
View File
@@ -0,0 +1,610 @@
"use client";
import {
ArrowDownRight,
ArrowLeftRight,
ArrowUpRight,
BarChart3,
Bell,
ChevronDown,
Copy,
Gift,
HelpCircle,
LayoutGrid,
LogOut,
type LucideIcon,
Menu,
Moon,
Search,
Settings,
Star,
TrendingUp,
Wallet,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
type NavLink = {
label: string;
href: string;
isActive?: boolean;
icon: LucideIcon;
};
type TokenItem = {
name: string;
symbol: string;
color: string;
};
type NotificationItem = {
title: string;
detail: string;
time: string;
tone: string;
};
const data = {
brand: {
name: "NexoChain",
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-2.webp",
},
wallet: {
address: "0x1cf2...9a56",
balance: "$460,890.02",
status: "متصل",
},
};
const navLinks: NavLink[] = [
{ label: "نمای کلی", href: "#", isActive: true, icon: LayoutGrid },
{ label: "بازارها", href: "#", icon: BarChart3 },
{ label: "معامله", href: "#", icon: ArrowLeftRight },
{ label: "پاداش‌ها", href: "#", icon: TrendingUp },
];
const favorites: TokenItem[] = [
{ name: "Bitcoin", symbol: "BTC", color: "bg-orange-500" },
{ name: "Ethereum", symbol: "ETH", color: "bg-indigo-500" },
{ name: "XRP", symbol: "XRP", color: "bg-neutral-900" },
{ name: "TetherUS", symbol: "IRRT", color: "bg-emerald-500" },
{ name: "Tron", symbol: "TRX", color: "bg-red-500" },
{ name: "Solana", symbol: "SOL", color: "bg-violet-500" },
{ name: "IRRC", symbol: "IRRC", color: "bg-blue-500" },
{ name: "BNB", symbol: "BNB", color: "bg-yellow-500" },
{ name: "Avalanche", symbol: "AVAX", color: "bg-rose-500" },
{ name: "Cardano", symbol: "ADA", color: "bg-sky-500" },
];
const walletAssets: TokenItem[] = [
{ name: "Bitcoin", symbol: "BTC", color: "bg-orange-500" },
{ name: "Ethereum", symbol: "ETH", color: "bg-indigo-500" },
{ name: "XRP", symbol: "XRP", color: "bg-neutral-900" },
{ name: "TetherUS", symbol: "IRRT", color: "bg-emerald-500" },
];
const notifications: NotificationItem[] = [
{
title: "برای امنیت حساب، احراز دو مرحله‌ای را فعال کنید",
detail: "کروم روی macOS، استانبول",
time: "۵ دقیقه",
tone: "text-rose-500",
},
{
title: "مبادله انجام شد",
detail: "۰٫۵ BTC - ۱۲٬۳۵۰ IRRC",
time: "اکنون",
tone: "text-emerald-500",
},
{
title: "۱۵ IRRC پاداش معاملاتی دریافت کردید",
detail: "در انتظار تأیید بلاک",
time: "۱ ساعت",
tone: "text-emerald-500",
},
{
title: "ورود جدید",
detail: "کروم روی macOS، استانبول",
time: "۱۰ دقیقه",
tone: "text-orange-500",
},
{
title: "تعمیرات برنامه‌ریزی‌شده",
detail: "فردا ساعت ۰۳:۰۰ UTC",
time: "دیروز",
tone: "text-muted-foreground",
},
];
function TokenBadge({ item }: { item: TokenItem }) {
return (
<span
className={cn(
"flex size-6 items-center justify-center rounded-full text-[11px] font-semibold text-white",
item.color,
)}
>
{item.symbol.slice(0, 1)}
</span>
);
}
function HeaderTab({ item }: { item: NavLink }) {
return (
<a
href={item.href}
className={cn(
"rounded-full px-3 py-1 text-sm",
item.isActive
? "bg-accent text-foreground"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
)}
>
{item.label}
</a>
);
}
function SearchField({ className }: { className?: string }) {
return (
<div className={cn("relative", className)}>
<Search className="pointer-events-none absolute top-1/2 start-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="جستجوی توکن یا جفت..."
className="h-9 w-full rounded-full bg-muted ps-9"
aria-label="جستجوی توکن"
/>
<span className="pointer-events-none absolute top-1/2 end-2.5 -translate-y-1/2 rounded-full bg-accent px-2 py-0.5 text-[10px] text-muted-foreground">
/
</span>
</div>
);
}
function WalletMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="gap-2 rounded-full border border-border bg-background px-3"
>
<span className="flex size-5 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-3.5" />
</span>
<span className="text-sm font-medium text-foreground">
{data.wallet.address}
</span>
<ChevronDown className="size-3 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-2">
<span className="flex size-9 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-4" />
</span>
<div>
<div className="text-sm font-medium text-foreground">
{data.wallet.address}
</div>
<div className="text-xs text-primary">{data.wallet.status}</div>
</div>
</div>
<Button variant="outline" size="sm" className="h-7 gap-1">
<Copy className="size-3" />
کپی
</Button>
</div>
<div className="mt-4 rounded-lg border border-border bg-muted px-3 py-2">
<div className="text-xs text-muted-foreground">موجودی کل</div>
<div className="mt-1 text-xl font-semibold text-foreground">
{data.wallet.balance}
</div>
<Button variant="outline" size="sm" className="mt-2 h-7 gap-1">
<span className="flex size-4 items-center justify-center rounded-full bg-primary text-[10px] text-primary-foreground">
E
</span>
ارز دیجیتال
<ChevronDown className="size-3" />
</Button>
</div>
<div className="mt-3 flex items-center gap-2">
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowLeftRight className="size-3.5" />
مبادله
</Button>
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowDownRight className="size-3.5" />
خرید
</Button>
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowUpRight className="size-3.5" />
ارسال
</Button>
</div>
<div className="mt-4 flex items-center justify-between text-xs font-medium text-muted-foreground">
<span>دارایی‌های برتر</span>
<span className="text-primary">مشاهده همه</span>
</div>
<div className="mt-2 space-y-2">
{walletAssets.map((asset) => (
<div
key={asset.symbol}
className="flex items-center justify-between rounded-md px-2 py-1 text-sm hover:bg-accent/50"
>
<div className="flex items-center gap-2">
<TokenBadge item={asset} />
<span>
{asset.name}
<span className="ms-1 text-xs text-muted-foreground">
{asset.symbol}
</span>
</span>
</div>
<span className="text-xs text-muted-foreground">۰٫۰۰۹۵</span>
</div>
))}
</div>
<DropdownMenuSeparator className="my-3" />
<DropdownMenuItem className="flex items-center justify-between">
<div>
<div className="text-sm">تأیید ۲FA</div>
<div className="text-xs text-muted-foreground">
برای امنیت بیشتر ۲FA را فعال کنید
</div>
</div>
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary">
تأیید
</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>تنظیمات کیف پول</DropdownMenuItem>
<DropdownMenuItem className="text-rose-500">
قطع اتصال کیف پول
</DropdownMenuItem>
<div className="mt-2 text-[11px] text-muted-foreground">
نسخه ۱٫۵٫۶۹ قوانین و مقررات
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function FavoritesMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full">
<Star className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">علاقه‌مندی‌های من (۴)</span>
<button className="text-xs text-muted-foreground">پاک کردن</button>
</div>
<div className="relative mt-3">
<Search className="pointer-events-none absolute top-1/2 start-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="جستجوی توکن..."
className="h-8 ps-8 text-sm"
aria-label="جستجوی توکن"
/>
</div>
<div className="mt-3 max-h-56 space-y-2 overflow-auto pe-1">
{favorites.map((token) => (
<div
key={token.symbol}
className="flex items-center justify-between rounded-md px-2 py-1 text-sm hover:bg-accent/50"
>
<div className="flex items-center gap-2">
<TokenBadge item={token} />
<span>
{token.name}
<span className="ms-1 text-xs text-muted-foreground">
{token.symbol}
</span>
</span>
</div>
<Star className="size-3 text-amber-400" />
</div>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function NotificationsMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full">
<Bell className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">اعلان‌ها</span>
<button className="text-xs text-primary">علامت‌گذاری همه به‌عنوان خوانده‌شده</button>
</div>
<div className="mt-3 space-y-2">
{notifications.map((item) => (
<div
key={item.title}
className="flex items-start gap-2 rounded-md border border-border px-2 py-2"
>
<span
aria-hidden="true"
className={cn(
"mt-1 flex size-7 items-center justify-center rounded-full bg-accent text-xs",
item.tone,
)}
/>
<div className="flex-1">
<div className="text-sm font-medium text-foreground">
{item.title}
</div>
<div className="text-xs text-muted-foreground">
{item.detail}
</div>
</div>
<span className="text-xs text-muted-foreground">{item.time}</span>
</div>
))}
</div>
<button className="mt-3 w-full rounded-md border border-border py-1 text-xs text-muted-foreground">
مشاهده همه اعلان‌ها
</button>
</DropdownMenuContent>
</DropdownMenu>
);
}
function UserMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 rounded-full border border-transparent px-2 py-1 text-sm hover:bg-accent/50">
<Avatar className="size-7">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback className="text-xs">
{data.user.name
.split(" ")
.map((part) => part[0])
.join("")}
</AvatarFallback>
</Avatar>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-64 p-3 text-start">
<div className="flex items-center gap-3">
<Avatar className="size-9">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback className="text-xs">
{data.user.name
.split(" ")
.map((part) => part[0])
.join("")}
</AvatarFallback>
</Avatar>
<div>
<div className="text-sm font-medium text-foreground">
{data.user.name}
</div>
<div className="text-xs text-muted-foreground">
{data.user.email}
</div>
</div>
</div>
<DropdownMenuSeparator className="my-2" />
<DropdownMenuItem className="gap-2">
<Moon className="size-4" />
حالت تیره
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Settings className="size-4" />
تنظیمات حساب
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Wallet className="size-4" />
ترجیح‌ها
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<HelpCircle className="size-4" />
راهنمای کاربران
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Gift className="size-4" />
انجمن
</DropdownMenuItem>
<DropdownMenuSeparator className="my-2" />
<DropdownMenuItem className="gap-2 text-rose-500">
<LogOut className="size-4" />
خروج
</DropdownMenuItem>
<div className="mt-2 text-[11px] text-muted-foreground">
نسخه ۲٫۸٫۶۹ قوانین و مقررات
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function MobileMenu() {
return (
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="باز کردن منو"
>
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" dir="rtl" className="w-72 p-0 text-start">
<SheetHeader className="px-4 pt-4">
<SheetTitle className="flex items-center gap-2">
<span className="flex size-8 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-4" />
</span>
{data.brand.name}
</SheetTitle>
</SheetHeader>
<ScrollArea className="min-h-0 flex-1">
<div className="flex flex-col gap-2 px-4 py-4">
{navLinks.map((link) => (
<a
key={link.label}
href={link.href}
className={cn(
"rounded-md px-3 py-2 text-sm",
link.isActive
? "bg-accent font-medium"
: "text-muted-foreground hover:bg-accent/50",
)}
>
{link.label}
</a>
))}
</div>
<div className="px-4 pb-4">
<WalletMenu />
</div>
</ScrollArea>
</SheetContent>
</Sheet>
);
}
function BottomNav() {
return (
<nav className="fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/95 backdrop-blur md:hidden">
<div className="grid grid-cols-4">
{navLinks.map((link) => {
const Icon = link.icon;
return (
<a
key={link.label}
href={link.href}
className={cn(
"flex flex-col items-center gap-1 py-2 text-xs",
link.isActive
? "text-primary"
: "text-muted-foreground hover:text-foreground",
)}
>
<Icon className="size-5" aria-hidden="true" />
<span className="sr-only">{link.label}</span>
<span aria-hidden="true">{link.label}</span>
</a>
);
})}
</div>
</nav>
);
}
interface ApplicationShell14Props {
className?: string;
}
export function ApplicationShell14({ className }: ApplicationShell14Props) {
return (
<div
dir="rtl"
lang="fa"
className={cn("min-h-svh bg-background", className)}
>
<div className="min-h-svh w-full bg-background">
<div className="border-b border-border">
<div className="mx-auto w-full max-w-7xl px-4">
<div className="grid grid-cols-[1fr_auto_1fr] items-center gap-3 py-2 text-xs text-muted-foreground">
<div />
<div className="flex flex-wrap items-center justify-center gap-2 text-center">
<Gift className="size-3.5 text-primary" />
<span>
۲۵ دلار پاداش خوش‌آمدگویی اولین مبادله را دریافت کنید
</span>
<button className="text-primary">دریافت کنید</button>
</div>
<div className="flex justify-end">
<button className="text-muted-foreground" aria-label="بستن">
&times;
</button>
</div>
</div>
</div>
</div>
<div className="border-b border-border">
<div className="mx-auto w-full max-w-7xl px-4">
<div className="flex items-center justify-between gap-4 py-3">
<div className="flex items-center gap-2 md:gap-3">
<MobileMenu />
<div className="flex size-9 items-center justify-center rounded-full bg-primary/10 text-primary md:hidden">
<Wallet className="size-4" />
</div>
<div className="hidden size-9 items-center justify-center rounded-full bg-primary/10 text-primary md:flex">
<Wallet className="size-5" />
</div>
<nav className="hidden items-center gap-1 md:flex">
{navLinks.map((link) => (
<HeaderTab key={link.label} item={link} />
))}
</nav>
</div>
<div className="hidden w-full justify-self-center md:block md:max-w-md">
<SearchField />
</div>
<div className="flex items-center gap-1 md:gap-2">
<WalletMenu />
<div className="hidden md:block">
<FavoritesMenu />
</div>
<div className="hidden md:block">
<NotificationsMenu />
</div>
<UserMenu />
</div>
</div>
</div>
</div>
<div className="border-b border-border md:hidden">
<div className="mx-auto w-full max-w-7xl px-4 py-3">
<SearchField />
</div>
</div>
<div className="mx-auto w-full max-w-7xl px-4">
<div className="min-h-[360px]" />
</div>
</div>
<BottomNav />
</div>
);
}
+422
View File
@@ -0,0 +1,422 @@
"use client";
import {
BadgeCheck,
BarChart3,
Briefcase,
ChevronRight,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
HelpCircle,
LayoutDashboard,
LogOut,
Settings,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
// Base nav item - used by simple sidebars
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
// Optional children for submenus (Sidebar3+)
children?: NavItem[];
};
// Nav group with optional collapsible state
type NavGroup = {
title: string;
items: NavItem[];
// Optional: default collapsed state (Sidebar2+)
defaultOpen?: boolean;
};
// User data for footer (Sidebar6+)
type UserData = {
name: string;
email: string;
avatar: string;
};
// Complete sidebar data structure
type SidebarData = {
// Logo/branding (all sidebars)
logo: {
src: string;
alt: string;
title: string;
description: string;
};
// Main navigation groups (all sidebars)
navGroups: NavGroup[];
// Footer navigation group (all sidebars)
footerGroup: NavGroup;
// User data for user footer (Sidebar6+)
user?: UserData;
// Workspaces for switcher (Sidebar7+)
workspaces?: Array<{
id: string;
name: string;
logo: string;
plan: string;
}>;
// Currently active workspace (Sidebar7+)
activeWorkspace?: string;
};
// Shared sidebar data - works with all sidebar variations
const sidebarData: SidebarData = {
logo: {
src: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
alt: "شاد‌سی‌ان بلاکس",
title: "ShadcnBlocks",
description: "اپلیکیشن خود را بسازید",
},
navGroups: [
{
title: "نمای کلی",
defaultOpen: true,
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
defaultOpen: true,
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۲۲۴", icon: FileText, href: "#" },
{ label: "بایگانی ۲۲۳", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
defaultOpen: false,
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
defaultOpen: false,
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
],
footerGroup: {
title: "پشتیبانی",
items: [
{ label: "راهنمای کاربران", icon: HelpCircle, href: "#" },
{ label: "تنظیمات", icon: Settings, href: "#" },
],
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-4.webp",
},
workspaces: [
{
id: "1",
name: "ShadcnBlocks",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "سازمانی",
},
{
id: "2",
name: "ری یو آی قالب‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "استارتاپ",
},
{
id: "3",
name: "ری یو آی بخش‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "رایگان",
},
],
activeWorkspace: "1",
};
const SidebarLogo = ({ logo }: { logo: SidebarData["logo"] }) => {
return (
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" tooltip={logo.title}>
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={logo.src}
alt={logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<div className="flex flex-col gap-0.5 text-start leading-none">
<span className="font-medium">{logo.title}</span>
<span className="text-xs text-muted-foreground">
{logo.description}
</span>
</div>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
);
};
const NavMenuItem = ({ item }: { item: NavItem }) => {
const Icon = item.icon;
const hasChildren = item.children && item.children.length > 0;
if (!hasChildren) {
return (
<SidebarMenuItem>
<SidebarMenuButton
asChild
isActive={item.isActive}
tooltip={item.label}
>
<a href={item.href}>
<Icon className="size-4" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
}
return (
<Collapsible asChild defaultOpen className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton isActive={item.isActive} tooltip={item.label}>
<Icon className="size-4" />
<span>{item.label}</span>
<ChevronRight className="ms-auto size-4 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.children!.map((child) => (
<SidebarMenuSubItem key={child.label}>
<SidebarMenuSubButton asChild isActive={child.isActive}>
<a href={child.href}>{child.label}</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
);
};
const NavUser = ({ user }: { user: UserData }) => {
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="size-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">
{user.email}
</span>
</div>
<ChevronsUpDown className="ms-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side="bottom"
align="start"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
<Avatar className="size-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">
{user.email}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
};
const AppSidebar = ({ ...props }: React.ComponentProps<typeof Sidebar>) => {
return (
<Sidebar side="right" variant="inset" collapsible="icon" {...props}>
<SidebarHeader>
<div className="flex items-center gap-2 group-data-[collapsible=icon]:flex-col">
<SidebarLogo logo={sidebarData.logo} />
<SidebarTrigger className="ms-auto group-data-[collapsible=icon]:ms-0" />
</div>
</SidebarHeader>
<SidebarContent className="overflow-hidden">
<ScrollArea className="min-h-0 flex-1">
{sidebarData.navGroups.map((group) => (
<SidebarGroup key={group.title}>
<SidebarGroupLabel>{group.title}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{group.items.map((item) => (
<NavMenuItem key={item.label} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
))}
</ScrollArea>
</SidebarContent>
<SidebarFooter>
{sidebarData.user && <NavUser user={sidebarData.user} />}
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
};
interface ApplicationShell2Props {
className?: string;
}
export function ApplicationShell2({ className }: ApplicationShell2Props) {
return (
<SidebarProvider dir="rtl" lang="fa" className={cn(className)}>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4 md:hidden">
<SidebarTrigger className="-ms-1" />
<Separator
orientation="vertical"
className="ms-2 data-[orientation=vertical]:h-4"
/>
<a href="#" className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{sidebarData.logo.title}</span>
</a>
</header>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</div>
</SidebarInset>
</SidebarProvider>
);
}
+444
View File
@@ -0,0 +1,444 @@
"use client";
import {
BadgeCheck,
BarChart3,
Briefcase,
ChevronDown,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
HelpCircle,
LayoutDashboard,
LogOut,
Menu,
Search,
Settings,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
// Base nav item - used by simple sidebars
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
// Optional children for submenus (Sidebar3+)
children?: NavItem[];
};
// Nav group with optional collapsible state
type NavGroup = {
title: string;
items: NavItem[];
// Optional: default collapsed state (Sidebar2+)
defaultOpen?: boolean;
};
// User data for footer (Sidebar6+)
type UserData = {
name: string;
email: string;
avatar: string;
};
// Complete sidebar data structure
type SidebarData = {
// Logo/branding (all sidebars)
logo: {
src: string;
alt: string;
title: string;
description: string;
};
// Main navigation groups (all sidebars)
navGroups: NavGroup[];
// Footer navigation group (all sidebars)
footerGroup: NavGroup;
// User data for user footer (Sidebar6+)
user?: UserData;
// Workspaces for switcher (Sidebar7+)
workspaces?: Array<{
id: string;
name: string;
logo: string;
plan: string;
}>;
// Currently active workspace (Sidebar7+)
activeWorkspace?: string;
};
// Shared sidebar data - works with all sidebar variations
const sidebarData: SidebarData = {
logo: {
src: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
alt: "شاد‌سی‌ان بلاکس",
title: "ShadcnBlocks",
description: "اپلیکیشن خود را بسازید",
},
navGroups: [
{
title: "نمای کلی",
defaultOpen: true,
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
defaultOpen: true,
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۲۲۴", icon: FileText, href: "#" },
{ label: "بایگانی ۲۲۳", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
defaultOpen: false,
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
defaultOpen: false,
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
],
footerGroup: {
title: "پشتیبانی",
items: [
{ label: "راهنمای کاربران", icon: HelpCircle, href: "#" },
{ label: "تنظیمات", icon: Settings, href: "#" },
],
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-5.webp",
},
workspaces: [
{
id: "1",
name: "ShadcnBlocks",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "سازمانی",
},
{
id: "2",
name: "ری یو آی قالب‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "استارتاپ",
},
{
id: "3",
name: "ری یو آی بخش‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "رایگان",
},
],
activeWorkspace: "1",
};
// Desktop navigation dropdown for each group
const NavDropdown = ({ group }: { group: NavGroup }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-1">
{group.title}
<ChevronDown className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48">
{group.items.map((item) => {
const Icon = item.icon;
return (
<React.Fragment key={item.label}>
<DropdownMenuItem asChild>
<a href={item.href} className="flex items-center gap-2">
<Icon className="size-4" />
{item.label}
</a>
</DropdownMenuItem>
{item.children?.map((child) => {
const ChildIcon = child.icon;
return (
<DropdownMenuItem key={child.label} asChild className="ps-6">
<a href={child.href} className="flex items-center gap-2">
<ChildIcon className="size-4" />
{child.label}
</a>
</DropdownMenuItem>
);
})}
</React.Fragment>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
};
// Mobile navigation sheet
const MobileNav = () => {
return (
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="باز کردن منو"
>
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" dir="rtl" className="w-72 p-0 text-start">
<SheetHeader className="px-4 pt-4">
<SheetTitle className="flex items-center gap-2 text-start">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
{sidebarData.logo.title}
</SheetTitle>
</SheetHeader>
<ScrollArea className="min-h-0 flex-1">
<nav className="flex flex-col gap-4 px-4 py-4">
{sidebarData.navGroups.map((group) => (
<div key={group.title}>
<div className="mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase">
{group.title}
</div>
<div className="flex flex-col gap-1">
{group.items.map((item) => {
const Icon = item.icon;
return (
<React.Fragment key={item.label}>
<a
href={item.href}
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted",
item.isActive && "bg-muted font-medium",
)}
>
<Icon className="size-4" />
{item.label}
</a>
{item.children?.map((child) => {
const ChildIcon = child.icon;
return (
<a
key={child.label}
href={child.href}
className="flex items-center gap-2 rounded-md py-1.5 pe-2 ps-8 text-sm hover:bg-muted"
>
<ChildIcon className="size-4" />
{child.label}
</a>
);
})}
</React.Fragment>
);
})}
</div>
</div>
))}
<Separator />
<div>
<div className="mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase">
{sidebarData.footerGroup.title}
</div>
<div className="flex flex-col gap-1">
{sidebarData.footerGroup.items.map((item) => {
const Icon = item.icon;
return (
<a
key={item.label}
href={item.href}
className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted"
>
<Icon className="size-4" />
{item.label}
</a>
);
})}
</div>
</div>
</nav>
</ScrollArea>
</SheetContent>
</Sheet>
);
};
// User dropdown
const NavUser = ({ user }: { user: UserData }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-2 px-2">
<Avatar className="size-8">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<span className="hidden text-sm font-medium md:inline">
{user.name}
</span>
<ChevronsUpDown className="hidden size-4 md:block" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1 text-start">
<p className="text-sm font-medium">{user.name}</p>
<p className="text-xs text-muted-foreground">{user.email}</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
interface ApplicationShell3Props {
className?: string;
}
export function ApplicationShell3({ className }: ApplicationShell3Props) {
return (
<div dir="rtl" lang="fa" className={cn("flex min-h-svh flex-col", className)}>
{/* Top navigation bar */}
<header className="sticky top-0 z-50 border-b bg-background">
<div className="flex h-14 items-center gap-4 px-4 lg:px-6">
{/* Mobile menu */}
<MobileNav />
{/* Logo */}
<a href="#" className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{sidebarData.logo.title}</span>
</a>
{/* Desktop navigation */}
<nav className="ms-4 hidden items-center gap-1 md:flex">
{sidebarData.navGroups.map((group) => (
<NavDropdown key={group.title} group={group} />
))}
</nav>
{/* Right side */}
<div className="ms-auto flex items-center gap-2">
<div className="relative hidden md:block">
<Search className="pointer-events-none absolute top-1/2 start-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
type="search"
placeholder="جستجو..."
aria-label="جستجو"
className="h-9 w-64 ps-8"
/>
</div>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="جستجو"
>
<Search className="size-5" />
</Button>
{sidebarData.user && <NavUser user={sidebarData.user} />}
</div>
</div>
</header>
{/* Main content */}
<main className="flex flex-1 flex-col gap-4 p-4 lg:p-6">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</main>
</div>
);
}
+539
View File
@@ -0,0 +1,539 @@
"use client";
import {
BadgeCheck,
BarChart3,
Briefcase,
ChevronDown,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
HelpCircle,
LayoutDashboard,
LogOut,
Menu,
Search,
Settings,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
// Base nav item - used by simple sidebars
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
// Optional children for submenus (Sidebar3+)
children?: NavItem[];
};
// Nav group with optional collapsible state
type NavGroup = {
title: string;
items: NavItem[];
// Optional: default collapsed state (Sidebar2+)
defaultOpen?: boolean;
};
// User data for footer (Sidebar6+)
type UserData = {
name: string;
email: string;
avatar: string;
};
// Complete sidebar data structure
type SidebarData = {
// Logo/branding (all sidebars)
logo: {
src: string;
alt: string;
title: string;
description: string;
};
// Main navigation groups (all sidebars)
navGroups: NavGroup[];
// Footer navigation group (all sidebars)
footerGroup: NavGroup;
// User data for user footer (Sidebar6+)
user?: UserData;
// Workspaces for switcher (Sidebar7+)
workspaces?: Array<{
id: string;
name: string;
logo: string;
plan: string;
}>;
// Currently active workspace (Sidebar7+)
activeWorkspace?: string;
};
// Shared sidebar data - works with all sidebar variations
const sidebarData: SidebarData = {
logo: {
src: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
alt: "شاد‌سی‌ان بلاکس",
title: "ShadcnBlocks",
description: "اپلیکیشن خود را بسازید",
},
navGroups: [
{
title: "نمای کلی",
defaultOpen: true,
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
defaultOpen: true,
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۲۲۴", icon: FileText, href: "#" },
{ label: "بایگانی ۲۲۳", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
defaultOpen: false,
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
defaultOpen: false,
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
],
footerGroup: {
title: "پشتیبانی",
items: [
{ label: "راهنمای کاربران", icon: HelpCircle, href: "#" },
{ label: "تنظیمات", icon: Settings, href: "#" },
],
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-6.webp",
},
workspaces: [
{
id: "1",
name: "ShadcnBlocks",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "سازمانی",
},
{
id: "2",
name: "ری یو آی قالب‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "استارتاپ",
},
{
id: "3",
name: "ری یو آی بخش‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "رایگان",
},
],
activeWorkspace: "1",
};
// Desktop navigation dropdown for each group
const NavDropdown = ({
group,
isActive,
onSelect,
}: {
group: NavGroup;
isActive: boolean;
onSelect: () => void;
}) => {
return (
<Button
variant="ghost"
className={cn("gap-1", isActive && "bg-muted")}
onClick={onSelect}
>
{group.title}
</Button>
);
};
// Sub-navigation row showing items for active group
const SubNav = ({
group,
activeItem,
onSelectItem,
}: {
group: NavGroup;
activeItem: NavItem | null;
onSelectItem: (item: NavItem) => void;
}) => {
return (
<div className="flex h-10 items-center gap-1 border-b bg-muted/30 px-4 lg:px-6">
{group.items.map((item) => {
const Icon = item.icon;
const isActive = activeItem?.label === item.label;
const hasChildren = item.children && item.children.length > 0;
if (hasChildren) {
return (
<DropdownMenu key={item.label}>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className={cn(
"h-7 gap-1.5 text-sm",
isActive && "bg-accent text-accent-foreground",
)}
>
<Icon className="size-4" />
{item.label}
<ChevronDown className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
{item.children!.map((child) => {
const ChildIcon = child.icon;
return (
<DropdownMenuItem key={child.label} asChild>
<a href={child.href} className="flex items-center gap-2">
<ChildIcon className="size-4" />
{child.label}
</a>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
return (
<Button
key={item.label}
variant="ghost"
size="sm"
className={cn(
"h-7 gap-1.5 text-sm",
isActive && "bg-accent text-accent-foreground",
)}
onClick={() => onSelectItem(item)}
>
<Icon className="size-4" />
{item.label}
</Button>
);
})}
</div>
);
};
// Mobile navigation sheet
const MobileNav = ({
activeGroupIndex,
onSelectGroup,
}: {
activeGroupIndex: number;
onSelectGroup: (index: number) => void;
}) => {
return (
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="باز کردن منو"
>
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" dir="rtl" className="w-72 p-0 text-start">
<SheetHeader className="px-4 pt-4">
<SheetTitle className="flex items-center gap-2 text-start">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
{sidebarData.logo.title}
</SheetTitle>
</SheetHeader>
<ScrollArea className="min-h-0 flex-1">
<nav className="flex flex-col gap-4 px-4 py-4">
{sidebarData.navGroups.map((group, index) => (
<div key={group.title}>
<button
onClick={() => onSelectGroup(index)}
className={cn(
"mb-2 text-xs font-medium tracking-wider uppercase",
index === activeGroupIndex
? "text-foreground"
: "text-muted-foreground",
)}
>
{group.title}
</button>
{index === activeGroupIndex && (
<div className="flex flex-col gap-1">
{group.items.map((item) => {
const Icon = item.icon;
return (
<React.Fragment key={item.label}>
<a
href={item.href}
className={cn(
"flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted",
item.isActive && "bg-muted font-medium",
)}
>
<Icon className="size-4" />
{item.label}
</a>
{item.children?.map((child) => {
const ChildIcon = child.icon;
return (
<a
key={child.label}
href={child.href}
className="flex items-center gap-2 rounded-md py-1.5 pe-2 ps-8 text-sm hover:bg-muted"
>
<ChildIcon className="size-4" />
{child.label}
</a>
);
})}
</React.Fragment>
);
})}
</div>
)}
</div>
))}
<Separator />
<div>
<div className="mb-2 text-xs font-medium tracking-wider text-muted-foreground uppercase">
{sidebarData.footerGroup.title}
</div>
<div className="flex flex-col gap-1">
{sidebarData.footerGroup.items.map((item) => {
const Icon = item.icon;
return (
<a
key={item.label}
href={item.href}
className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted"
>
<Icon className="size-4" />
{item.label}
</a>
);
})}
</div>
</div>
</nav>
</ScrollArea>
</SheetContent>
</Sheet>
);
};
// User dropdown
const NavUser = ({ user }: { user: UserData }) => {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="gap-2 px-2">
<Avatar className="size-8">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<span className="hidden text-sm font-medium md:inline">
{user.name}
</span>
<ChevronsUpDown className="hidden size-4 md:block" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1 text-start">
<p className="text-sm font-medium">{user.name}</p>
<p className="text-xs text-muted-foreground">{user.email}</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
interface ApplicationShell4Props {
className?: string;
}
export function ApplicationShell4({ className }: ApplicationShell4Props) {
const [activeGroupIndex, setActiveGroupIndex] = React.useState(0);
const activeGroup = sidebarData.navGroups[activeGroupIndex];
// Initialize activeItem to Dashboard (first item with isActive, or first item)
const getDefaultActiveItem = (group: NavGroup) => {
return group.items.find((item) => item.isActive) || group.items[0] || null;
};
const [activeItem, setActiveItem] = React.useState<NavItem | null>(
getDefaultActiveItem(activeGroup),
);
return (
<div dir="rtl" lang="fa" className={cn("flex min-h-svh flex-col", className)}>
{/* Top navigation bar */}
<header className="sticky top-0 z-50 bg-background">
<div className="flex h-14 items-center gap-4 border-b px-4 lg:px-6">
{/* Mobile menu */}
<MobileNav
activeGroupIndex={activeGroupIndex}
onSelectGroup={setActiveGroupIndex}
/>
{/* Logo */}
<a href="#" className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{sidebarData.logo.title}</span>
</a>
{/* Desktop navigation - group tabs */}
<nav className="ms-4 hidden items-center gap-1 md:flex">
{sidebarData.navGroups.map((group, index) => (
<NavDropdown
key={group.title}
group={group}
isActive={index === activeGroupIndex}
onSelect={() => setActiveGroupIndex(index)}
/>
))}
</nav>
{/* Right side */}
<div className="ms-auto flex items-center gap-2">
<div className="relative hidden md:block">
<Search className="pointer-events-none absolute top-1/2 start-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
type="search"
placeholder="جستجو..."
aria-label="جستجو"
className="h-9 w-64 ps-8"
/>
</div>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="جستجو"
>
<Search className="size-5" />
</Button>
{sidebarData.user && <NavUser user={sidebarData.user} />}
</div>
</div>
{/* Sub-navigation row */}
<SubNav
group={activeGroup}
activeItem={activeItem}
onSelectItem={setActiveItem}
/>
</header>
{/* Main content */}
<main className="flex flex-1 flex-col gap-4 p-4 lg:p-6">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</main>
</div>
);
}
+586
View File
@@ -0,0 +1,586 @@
"use client";
import {
BadgeCheck,
BarChart3,
Briefcase,
ChevronRight,
ChevronsUpDown,
ClipboardList,
Clock3,
FileText,
Folder,
Globe2,
HelpCircle,
LayoutDashboard,
LogOut,
Settings,
Sparkles,
Star,
User,
Users,
} from "lucide-react";
import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
// Base nav item - used by simple sidebars
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
isActive?: boolean;
// Optional children for submenus (Sidebar3+)
children?: NavItem[];
};
// Nav group with optional collapsible state
type NavGroup = {
title: string;
items: NavItem[];
// Optional: default collapsed state (Sidebar2+)
defaultOpen?: boolean;
};
// User data for footer (Sidebar6+)
type UserData = {
name: string;
email: string;
avatar: string;
};
// Complete sidebar data structure
type SidebarData = {
// Logo/branding (all sidebars)
logo: {
src: string;
alt: string;
title: string;
description: string;
};
// Main navigation groups (all sidebars)
navGroups: NavGroup[];
// Footer navigation group (all sidebars)
footerGroup: NavGroup;
// User data for user footer (Sidebar6+)
user?: UserData;
// Workspaces for switcher (Sidebar7+)
workspaces?: Array<{
id: string;
name: string;
logo: string;
plan: string;
}>;
// Currently active workspace (Sidebar7+)
activeWorkspace?: string;
};
// Shared sidebar data - works with all sidebar variations
const sidebarData: SidebarData = {
logo: {
src: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
alt: "شاد‌سی‌ان بلاکس",
title: "ShadcnBlocks",
description: "اپلیکیشن خود را بسازید",
},
navGroups: [
{
title: "نمای کلی",
defaultOpen: true,
items: [
{
label: "داشبورد",
icon: LayoutDashboard,
href: "#",
isActive: true,
},
{ label: "وظایف", icon: ClipboardList, href: "#" },
{ label: "نقشه راه", icon: BarChart3, href: "#" },
],
},
{
title: "پروژه‌ها",
defaultOpen: true,
items: [
{
label: "پروژه‌های فعال",
icon: Briefcase,
href: "#",
children: [
{ label: "پروژه آلفا", icon: FileText, href: "#" },
{ label: "پروژه بتا", icon: FileText, href: "#" },
{ label: "پروژه گاما", icon: FileText, href: "#" },
],
},
{
label: "بایگانی",
icon: Folder,
href: "#",
children: [
{ label: "بایگانی ۲۲۴", icon: FileText, href: "#" },
{ label: "بایگانی ۲۲۳", icon: FileText, href: "#" },
],
},
],
},
{
title: "تیم",
defaultOpen: false,
items: [
{ label: "اعضا", icon: Users, href: "#" },
{ label: "اسپرینت‌ها", icon: Clock3, href: "#" },
{ label: "تأییدها", icon: BadgeCheck, href: "#" },
{ label: "بازبینی‌ها", icon: Star, href: "#" },
],
},
{
title: "فضای کار",
defaultOpen: false,
items: [
{ label: "یکپارچه‌سازی‌ها", icon: Globe2, href: "#" },
{ label: "خودکارسازی‌ها", icon: Sparkles, href: "#" },
],
},
],
footerGroup: {
title: "پشتیبانی",
items: [
{ label: "راهنمای کاربران", icon: HelpCircle, href: "#" },
{ label: "تنظیمات", icon: Settings, href: "#" },
],
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-8.webp",
},
workspaces: [
{
id: "1",
name: "ShadcnBlocks",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "سازمانی",
},
{
id: "2",
name: "ری یو آی قالب‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "استارتاپ",
},
{
id: "3",
name: "ری یو آی بخش‌ها",
logo: "/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg",
plan: "رایگان",
},
],
activeWorkspace: "1",
};
const NavMenuItem = ({ item }: { item: NavItem }) => {
const Icon = item.icon;
const hasChildren = item.children && item.children.length > 0;
if (hasChildren) {
return (
<Collapsible asChild defaultOpen className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton isActive={item.isActive}>
<Icon className="size-4" />
<span>{item.label}</span>
<ChevronRight className="ms-auto size-4 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.children!.map((child) => {
const ChildIcon = child.icon;
return (
<SidebarMenuSubItem key={child.label}>
<SidebarMenuSubButton asChild isActive={child.isActive}>
<a href={child.href}>
<ChildIcon className="size-4" />
<span>{child.label}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
);
})}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
);
}
return (
<SidebarMenuItem>
<SidebarMenuButton asChild isActive={item.isActive}>
<a href={item.href}>
<Icon className="size-4" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
};
const NavUser = ({ user }: { user: UserData }) => {
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="size-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">
{user.email}
</span>
</div>
<ChevronsUpDown className="ms-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
side="bottom"
align="start"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-start text-sm">
<Avatar className="size-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">
{user.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs text-muted-foreground">
{user.email}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="me-2 size-4" />
حساب کاربری
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="me-2 size-4" />
خروج
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
};
// Module tabs (top bar navigation)
interface ModuleTabsProps {
className?: string;
groups: NavGroup[];
activeGroupIndex: number;
onChange: (index: number) => void;
}
const ModuleTabs = ({
className,
groups,
activeGroupIndex,
onChange,
}: ModuleTabsProps) => {
return (
<nav
aria-label="ماژول‌های اصلی"
className={cn(
"hidden flex-1 items-center gap-1 overflow-x-auto whitespace-nowrap md:flex",
className,
)}
>
{groups.map((group, index) => {
const isActive = index === activeGroupIndex;
return (
<button
key={group.title}
type="button"
onClick={() => onChange(index)}
className={cn(
"flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-colors",
isActive
? "bg-muted text-foreground"
: "text-muted-foreground hover:bg-muted/50 hover:text-foreground",
)}
>
<span>{group.title}</span>
</button>
);
})}
</nav>
);
};
// Mobile bottom navigation
interface MobileBottomNavProps {
className?: string;
groups: NavGroup[];
activeGroupIndex: number;
onChange: (index: number) => void;
}
const MobileBottomNav = ({
className,
groups,
activeGroupIndex,
onChange,
}: MobileBottomNavProps) => {
const { setOpenMobile } = useSidebar();
return (
<nav
aria-label="ناوبری پایین موبایل"
className={cn(
"fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 backdrop-blur md:hidden",
className,
)}
>
<div
className="grid"
style={{
gridTemplateColumns: `repeat(${groups.length}, minmax(0, 1fr))`,
}}
>
{groups.map((group, index) => {
// Use first item's icon as the group icon
const Icon = group.items[0]?.icon;
const isActive = index === activeGroupIndex;
return (
<button
key={group.title}
type="button"
onClick={() => {
onChange(index);
setOpenMobile(false);
}}
className={cn(
"flex flex-col items-center gap-1 py-2 text-xs transition-colors",
isActive
? "text-foreground"
: "text-muted-foreground hover:text-foreground",
)}
aria-label={group.title}
>
{Icon && <Icon className="size-5" />}
<span className="truncate">{group.title}</span>
</button>
);
})}
</div>
</nav>
);
};
interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
activeGroupIndex: number;
}
const AppSidebar = ({ activeGroupIndex, ...props }: AppSidebarProps) => {
const activeGroup = sidebarData.navGroups[activeGroupIndex];
const { isMobile } = useSidebar();
return (
<Sidebar
side="right"
collapsible="icon"
className="top-14 h-[calc(100svh-3.5rem)]!"
{...props}
>
<SidebarHeader>
<div className="flex items-center justify-between group-data-[collapsible=icon]:justify-center">
{isMobile ? (
<a href="#" className="flex items-center gap-2 px-2">
<div className="flex aspect-square size-6 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-4 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="text-sm font-medium">
{sidebarData.logo.title}
</span>
</a>
) : (
<span className="px-2 text-start text-sm font-medium group-data-[collapsible=icon]:hidden">
{activeGroup.title}
</span>
)}
<SidebarTrigger />
</div>
</SidebarHeader>
<SidebarContent className="overflow-hidden">
<ScrollArea className="min-h-0 flex-1">
<SidebarGroup>
<SidebarGroupLabel>{activeGroup.title}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{activeGroup.items.map((item) => (
<NavMenuItem key={item.label} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
{/* Footer group items */}
<SidebarGroup>
<SidebarGroupLabel>
{sidebarData.footerGroup.title}
</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{sidebarData.footerGroup.items.map((item) => {
const Icon = item.icon;
return (
<SidebarMenuItem key={item.label}>
<SidebarMenuButton asChild isActive={item.isActive}>
<a href={item.href}>
<Icon className="size-4" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</ScrollArea>
</SidebarContent>
<SidebarFooter>
{sidebarData.user && <NavUser user={sidebarData.user} />}
</SidebarFooter>
</Sidebar>
);
};
interface ApplicationShell5Props {
className?: string;
}
const ApplicationShell5 = ({ className }: ApplicationShell5Props) => {
const [activeGroupIndex, setActiveGroupIndex] = React.useState(0);
const activeGroup = sidebarData.navGroups[activeGroupIndex];
return (
<SidebarProvider dir="rtl" lang="fa" className={cn("flex flex-col", className)}>
<header className="sticky top-0 z-40 border-b bg-background">
<div className="flex h-14 items-center gap-4 px-4">
<div className="flex items-center gap-3 md:hidden">
<SidebarTrigger />
<a href="#" className="flex items-center gap-2">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{sidebarData.logo.title}</span>
</a>
</div>
<div className="hidden items-center gap-3 md:flex">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src={sidebarData.logo.src}
alt={sidebarData.logo.alt}
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="font-semibold">{sidebarData.logo.title}</span>
</div>
<ModuleTabs
groups={sidebarData.navGroups}
activeGroupIndex={activeGroupIndex}
onChange={setActiveGroupIndex}
/>
<div className="ms-auto flex items-center text-sm font-semibold md:hidden">
{activeGroup.title}
</div>
</div>
</header>
<div className="flex flex-1">
<AppSidebar activeGroupIndex={activeGroupIndex} />
<SidebarInset className="pb-20 md:pb-0">
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</div>
</SidebarInset>
</div>
<MobileBottomNav
groups={sidebarData.navGroups}
activeGroupIndex={activeGroupIndex}
onChange={setActiveGroupIndex}
/>
</SidebarProvider>
);
};
export { ApplicationShell5 };
+511
View File
@@ -0,0 +1,511 @@
"use client";
import { motion } from "framer-motion";
import {
Activity,
Calendar,
Clapperboard,
FileText,
Handshake,
Home,
Images,
Inbox,
Kanban,
LayoutDashboard,
ListTodo,
Receipt,
Settings,
Wallet,
} from "lucide-react";
import * as React from "react";
import {
Drawer,
DrawerContent,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
type ModuleId =
| "overview"
| "sales"
| "production"
| "deliverables"
| "finance";
type NavItem = {
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
href: string;
badge?: string;
active?: boolean;
};
type NavSection = {
title?: string;
items: NavItem[];
};
type NavModule = {
id: ModuleId;
label: string;
icon: NavItem["icon"];
sections: NavSection[];
};
const navModules: NavModule[] = [
{
id: "overview",
label: "نمای کلی",
icon: LayoutDashboard,
sections: [
{
title: "نمای کلی",
items: [
{ label: "خانه", icon: Home, href: "#", active: true },
{ label: "داشبورد", icon: Activity, href: "#" },
{ label: "صندوق ورودی", icon: Inbox, href: "#", badge: "۳" },
],
},
{
title: "وظایف",
items: [
{ label: "وظایف من", icon: ListTodo, href: "#", badge: "۵" },
{ label: "تقویم", icon: Calendar, href: "#" },
],
},
],
},
{
id: "sales",
label: "فروش",
icon: Handshake,
sections: [
{
title: "قیف فروش",
items: [
{ label: "سرنخ‌های جدید", icon: Inbox, href: "#", active: true },
{ label: "پیش‌فاکتورها", icon: FileText, href: "#" },
{ label: "رزروها", icon: Calendar, href: "#" },
],
},
{
title: "گزارش‌ها",
items: [{ label: "داشبورد فروش", icon: Activity, href: "#" }],
},
],
},
{
id: "production",
label: "تولید",
icon: Clapperboard,
sections: [
{
title: "مسیر تولید",
items: [
{ label: "تخته فیلمبرداری", icon: Kanban, href: "#", active: true },
{ label: "لیست شات‌ها", icon: ListTodo, href: "#" },
],
},
{
title: "تیم",
items: [{ label: "تخصیص‌ها", icon: Inbox, href: "#" }],
},
],
},
{
id: "deliverables",
label: "تحویل‌ها",
icon: Images,
sections: [
{
title: "گالری‌ها",
items: [
{ label: "همه گالری‌ها", icon: Images, href: "#", active: true },
{ label: "تأییدها", icon: Inbox, href: "#", badge: "۲" },
],
},
{
title: "خروجی‌ها",
items: [{ label: "مرکز دانلود", icon: FileText, href: "#" }],
},
],
},
{
id: "finance",
label: "مالی",
icon: Wallet,
sections: [
{
title: "صورتحساب",
items: [
{ label: "فاکتورها", icon: Receipt, href: "#", active: true },
{ label: "پرداخت‌ها", icon: Wallet, href: "#" },
],
},
{
title: "گزارش‌ها",
items: [{ label: "سود و زیان", icon: Activity, href: "#" }],
},
],
},
];
const utilities: NavItem[] = [{ label: "تنظیمات", icon: Settings, href: "#" }];
interface ModulePanelProps {
className?: string;
module: NavModule;
}
function ModulePanel({ className, module }: ModulePanelProps) {
return (
<div className={cn("space-y-4 px-4 py-3 text-start text-sm", className)}>
{module.sections.map((section, index) => (
<div className="space-y-2" key={section.title ?? index}>
{section.title && (
<SidebarGroupLabel className="mb-1 text-xs font-medium tracking-wide text-muted-foreground">
{section.title}
</SidebarGroupLabel>
)}
<SidebarMenu>
{section.items.map((item) => {
const Icon = item.icon;
return (
<SidebarMenuItem
key={item.label}
className="text-muted-foreground hover:text-foreground"
>
<SidebarMenuButton
asChild
isActive={item.active}
className="px-2"
>
<a href={item.href}>
<Icon className="size-4" />
{item.badge && (
<SidebarMenuBadge>{item.badge}</SidebarMenuBadge>
)}
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</div>
))}
<div className="border-t pt-3">
<SidebarGroupLabel className="mb-2 text-xs font-medium tracking-wide text-muted-foreground">
استودیو
</SidebarGroupLabel>
<SidebarMenu className="text-sm">
{utilities.map((item) => {
const Icon = item.icon;
return (
<SidebarMenuItem key={item.label}>
<SidebarMenuButton
asChild
className="gap-2.5 px-2 text-muted-foreground hover:text-foreground"
>
<a href={item.href}>
<Icon className="size-4" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</div>
</div>
);
}
interface AppSidebarProps extends Omit<
React.ComponentProps<typeof Sidebar>,
"className"
> {
className?: string;
activeKey: ModuleId;
onNavChange: (key: ModuleId) => void;
}
export function AppSidebar({
className,
activeKey,
onNavChange,
...props
}: AppSidebarProps) {
const { state, setOpen, isMobile } = useSidebar();
const isCollapsed = state === "collapsed";
const showFooter = isCollapsed || isMobile;
const activeModule =
navModules.find((module) => module.id === activeKey) ?? navModules[0];
const activeNavLabel = activeModule.label;
return (
<Sidebar
side="right"
collapsible="icon"
className={cn(
"overflow-hidden *:data-[sidebar=sidebar]:flex-row",
className,
)}
{...props}
>
<Sidebar
collapsible="none"
className="w-[calc(var(--sidebar-width-icon)+1px)]! border-e"
>
<SidebarHeader className="h-14 border-b">
<SidebarMenu className="my-auto">
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild className="md:h-8 md:p-0">
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
<span className="text-lg font-semibold tracking-tighter">
ری یو آی
</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent className="px-1.5 md:px-0">
<SidebarMenu>
{" "}
<SidebarMenuItem>
<motion.div
key={isCollapsed ? "h-collapsed" : "h-expanded"}
className={cn(
isCollapsed
? "hidden items-center justify-center sm:flex"
: "hidden",
)}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8 }}
>
<SidebarTrigger />
</motion.div>
</SidebarMenuItem>
{navModules.map((module) => {
const Icon = module.icon;
return (
<SidebarMenuItem key={module.id}>
<SidebarMenuButton
tooltip={{
children: module.label,
hidden: isMobile,
}}
onClick={() => {
onNavChange(module.id);
setOpen(true);
}}
isActive={activeKey === module.id}
aria-label={module.label}
className="px-2.5 md:px-2"
>
<Icon className="size-4" />
<span>{module.label}</span>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter className={cn(showFooter ? "flex" : "hidden")}>
<SidebarGroup className="p-0">
<SidebarGroupContent className="px-1.5 md:px-0">
<SidebarMenu>
{utilities.map((item) => {
const Icon = item.icon;
return (
<SidebarMenuItem key={item.label}>
<SidebarMenuButton
asChild
tooltip={{
children: item.label,
hidden: isMobile,
}}
className="px-2.5 md:px-2"
aria-label={item.label}
>
<a href={item.href}>
<Icon className="size-4" />
<span>{item.label}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>{" "}
</SidebarGroupContent>
</SidebarGroup>
</SidebarFooter>
</Sidebar>
<Sidebar collapsible="none" className="hidden flex-1 md:flex">
<SidebarHeader className="h-14 gap-3.5 border-b px-4">
<div className="my-auto flex w-full items-center justify-between">
<div className="text-start text-base font-medium text-foreground">
{activeNavLabel}
</div>
<div className="hidden sm:block">
<SidebarTrigger />
</div>
</div>
</SidebarHeader>
<SidebarContent className="overflow-hidden">
<ScrollArea className="min-h-0 flex-1">
<SidebarGroup className="px-0">
<SidebarGroupContent>
<ModulePanel module={activeModule} />
</SidebarGroupContent>
</SidebarGroup>
</ScrollArea>
</SidebarContent>
</Sidebar>
</Sidebar>
);
}
interface ApplicationShell6Props {
className?: string;
}
export function ApplicationShell6({ className }: ApplicationShell6Props) {
const [activeKey, setActiveKey] = React.useState<ModuleId>(navModules[0].id);
const [isMobilePanelOpen, setIsMobilePanelOpen] = React.useState(false);
const activeModule =
navModules.find((module) => module.id === activeKey) ?? navModules[0];
const activeNavLabel = activeModule.label;
return (
<SidebarProvider
dir="rtl"
lang="fa"
style={
{
"--sidebar-width": "350px",
} as React.CSSProperties
}
className={cn(className)}
>
<div className="hidden md:block">
<AppSidebar activeKey={activeKey} onNavChange={setActiveKey} />
</div>
<SidebarInset className="pb-20 md:pb-0">
<div className="flex flex-1 flex-col gap-4">
<div className="sticky top-0 z-40 flex h-14 items-center gap-2 border-b bg-background px-4 md:hidden">
<SidebarTrigger />
<Separator
orientation="vertical"
className="ms-2 data-[orientation=vertical]:h-4"
/>
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-6 text-primary-foreground invert dark:invert-0"
/>
</div>
</a>
<div className="ms-auto text-sm font-medium text-muted-foreground">
{activeNavLabel}
</div>
</div>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</div>
</div>
</SidebarInset>
<Drawer
open={isMobilePanelOpen}
onOpenChange={setIsMobilePanelOpen}
dismissible
>
<DrawerContent dir="rtl" className="text-start md:hidden">
<DrawerHeader>
<DrawerTitle className="text-start">{activeNavLabel}</DrawerTitle>
</DrawerHeader>
<div className="pb-6">
<ModulePanel module={activeModule} />
</div>
</DrawerContent>
</Drawer>
<nav
aria-label="ناوبری پایین موبایل"
className="fixed inset-x-0 bottom-0 z-40 border-t bg-background/95 backdrop-blur md:hidden"
>
<div className="grid grid-cols-5">
{navModules.map((module) => {
const Icon = module.icon;
const isActive = module.id === activeKey;
return (
<button
key={module.id}
type="button"
onClick={() => {
setActiveKey(module.id);
setIsMobilePanelOpen(true);
}}
className={`flex flex-col items-center justify-center py-3 text-xs ${
isActive
? "text-foreground"
: "text-muted-foreground hover:text-foreground"
}`}
aria-label={module.label}
>
<Icon className="h-5 w-5" />
<span className="sr-only">{module.label}</span>
</button>
);
})}
</div>
</nav>
</SidebarProvider>
);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+733
View File
@@ -0,0 +1,733 @@
"use client";
import {
ChevronRight,
File,
Folder,
GitBranch,
History,
MessageSquare,
PanelRight,
Play,
Plus,
Search,
Settings,
User,
} from "lucide-react";
import * as React from "react";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
Drawer,
DrawerContent,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarProvider,
} from "@/components/ui/sidebar";
import { cn } from "@/lib/utils";
type SidebarModule = {
id: string;
label: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
};
const sidebarModules: SidebarModule[] = [
{ id: "search", label: "جستجو", icon: Search },
{ id: "explorer", label: "مرورگر", icon: File },
{ id: "git", label: "کنترل منبع", icon: GitBranch },
];
const changesData = [
{ file: "README.md", state: "M" },
{ file: "api/hello/route.ts", state: "U" },
{ file: "app/layout.tsx", state: "M" },
];
type TreeItem = string | TreeItem[];
const fileTreeData: TreeItem[] = [
[
"app",
[
"api",
["hello", ["route.ts"]],
"page.tsx",
"layout.tsx",
["blog", ["page.tsx"]],
],
],
["components", ["ui", "button.tsx", "card.tsx"], "header.tsx", "footer.tsx"],
["lib", ["util.ts"]],
["public", "favicon.ico", "vercel.svg"],
".eslintrc.json",
".gitignore",
"next.config.js",
"tailwind.config.js",
"package.json",
"README.md",
];
function Logo({ className }: { className?: string }) {
return (
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className={cn("size-5 dark:invert", className)}
/>
);
}
function Tree({ item }: { item: TreeItem }) {
const [name, ...items] = Array.isArray(item) ? item : [item];
if (!items.length) {
return (
<SidebarMenuButton className="data-[active=true]:bg-transparent">
<File className="size-4" />
{name}
</SidebarMenuButton>
);
}
return (
<SidebarMenuItem>
<Collapsible
className="group/collapsible [&[data-state=open]>button>svg:first-child]:rotate-90"
defaultOpen={name === "app" || name === "components"}
>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<ChevronRight className="size-4 transition-transform rtl:rotate-180" />
<Folder className="size-4" />
{name}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{items.map((subItem, index) => (
<Tree key={index} item={subItem} />
))}
</SidebarMenuSub>
</CollapsibleContent>
</Collapsible>
</SidebarMenuItem>
);
}
interface AppSidebarProps extends React.ComponentProps<typeof Sidebar> {
activeModule: string;
onModuleChange: (moduleId: string) => void;
}
export function AppSidebar({
activeModule,
onModuleChange,
...props
}: AppSidebarProps) {
return (
<Sidebar side="right" collapsible="icon" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild className="md:h-8 md:p-0">
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-5 invert dark:invert-0"
/>
</div>
<div className="grid flex-1 text-start text-sm leading-tight">
<span className="truncate font-medium">ری یو آی</span>
<span className="truncate text-xs">پروژه من</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent className="px-1.5 md:px-0">
<SidebarMenu>
{sidebarModules.map((module) => (
<SidebarMenuItem key={module.id}>
<SidebarMenuButton
tooltip={{
children: module.label,
hidden: false,
}}
onClick={() => onModuleChange(module.id)}
isActive={activeModule === module.id}
className="px-2.5 md:px-2"
>
<module.icon />
<span>{module.label}</span>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
tooltip={{
children: "تنظیمات",
hidden: false,
}}
className="px-2.5 md:px-2"
aria-label="تنظیمات"
>
<Settings />
<span>تنظیمات</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton
tooltip={{
children: "پروفایل",
hidden: false,
}}
className="px-2.5 md:px-2"
aria-label="پروفایل"
>
<User />
<span>پروفایل</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
);
}
function ExplorerSidebar() {
return (
<Sidebar
collapsible="none"
className="w-full shrink-0 border-e md:flex md:w-[280px]"
>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>تغییرات</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{changesData.map((item, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuButton>
<File className="size-4" />
<span className="truncate">{item.file}</span>
</SidebarMenuButton>
<SidebarMenuBadge
className={cn(
"text-[10px] font-medium",
item.state === "M" && "text-yellow-500",
item.state === "U" && "text-green-500",
item.state === "D" && "text-red-500",
)}
>
{item.state}
</SidebarMenuBadge>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>فایل‌ها</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{fileTreeData.map((item, index) => (
<Tree key={index} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
function SearchSidebar() {
return (
<Sidebar
collapsible="none"
className="w-full shrink-0 border-e md:flex md:w-[280px]"
>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>جستجو</SidebarGroupLabel>
<SidebarGroupContent className="px-2">
<div className="flex items-center rounded-md border bg-background px-3 py-2">
<Search className="me-2 size-4 shrink-0 text-muted-foreground" />
<input
type="search"
placeholder="جستجوی فایل‌ها..."
aria-label="جستجوی فایل‌ها"
className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
/>
</div>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
function GitSidebar() {
return (
<Sidebar
collapsible="none"
className="w-full shrink-0 border-e md:flex md:w-[280px]"
>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>تغییرات</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{changesData.map((item, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuButton>
<File className="size-4" />
<span className="truncate">{item.file}</span>
</SidebarMenuButton>
<SidebarMenuBadge
className={cn(
"text-[10px] font-medium",
item.state === "M" && "text-yellow-500",
item.state === "U" && "text-green-500",
item.state === "D" && "text-red-500",
)}
>
{item.state}
</SidebarMenuBadge>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
type MobileDrawer = "search" | "explorer" | "git" | "chat" | null;
export function ApplicationShell9() {
const [activeModule, setActiveModule] = React.useState("explorer");
const [isChatOpen, setIsChatOpen] = React.useState(false);
const [mobileDrawer, setMobileDrawer] = React.useState<MobileDrawer>(null);
const handleModuleChange = (moduleId: string) => {
setActiveModule(moduleId);
};
const renderSidebar = () => {
switch (activeModule) {
case "search":
return <SearchSidebar />;
case "explorer":
return <ExplorerSidebar />;
case "کنترل نسخه":
return <GitSidebar />;
default:
return <ExplorerSidebar />;
}
};
return (
<SidebarProvider
dir="rtl"
lang="fa"
className="h-svh overflow-hidden"
style={
{
"--sidebar-width": "var(--sidebar-width-icon)",
} as React.CSSProperties
}
>
<AppSidebar
activeModule={activeModule}
onModuleChange={handleModuleChange}
className="hidden md:flex"
/>
<div className="flex h-full w-full flex-col md:hidden">
<header className="flex h-12 shrink-0 items-center justify-between border-b bg-background px-3">
<div className="flex items-center gap-2">
<div className="flex aspect-square size-7 items-center justify-center rounded-sm bg-primary">
<img
src="/block-media/shadcnblocks/block/logos/ShadcnBlocks-logo.svg"
alt="شاد‌سی‌ان بلاکس"
className="size-4 invert dark:invert-0"
/>
</div>
<span className="text-sm font-medium">پروژه من</span>
</div>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
className="size-8"
aria-label="افزودن"
>
<Plus className="size-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="size-8"
aria-label="تنظیمات"
>
<Settings className="size-4" />
</Button>
</div>
</header>
<div className="flex flex-1 items-center justify-center bg-muted/30">
<Logo className="size-16 opacity-30" />
</div>
<div className="flex h-14 shrink-0 items-center justify-around border-t bg-background">
<button
type="button"
onClick={() => setMobileDrawer("search")}
className={cn(
"flex items-center justify-center rounded-md p-2",
mobileDrawer === "search"
? "text-foreground"
: "text-muted-foreground",
)}
aria-label="جستجو"
>
<Search className="size-5" />
<span className="sr-only">جستجو</span>
</button>
<button
type="button"
onClick={() => setMobileDrawer("explorer")}
className={cn(
"flex items-center justify-center rounded-md p-2",
mobileDrawer === "explorer"
? "text-foreground"
: "text-muted-foreground",
)}
aria-label="مرورگر"
>
<File className="size-5" />
<span className="sr-only">مرورگر</span>
</button>
<button
type="button"
onClick={() => setMobileDrawer("git")}
className={cn(
"flex items-center justify-center rounded-md p-2",
mobileDrawer === "git"
? "text-foreground"
: "text-muted-foreground",
)}
aria-label="کنترل منبع"
>
<GitBranch className="size-5" />
<span className="sr-only">کنترل منبع</span>
</button>
<button
type="button"
onClick={() => setMobileDrawer("chat")}
className={cn(
"flex items-center justify-center rounded-md p-2",
mobileDrawer === "chat"
? "text-foreground"
: "text-muted-foreground",
)}
aria-label="گفتگو"
>
<MessageSquare className="size-5" />
<span className="sr-only">گفتگو</span>
</button>
</div>
<Drawer
open={mobileDrawer === "search"}
onOpenChange={(open) => setMobileDrawer(open ? "search" : null)}
>
<DrawerContent dir="rtl" className="text-start">
<DrawerHeader>
<DrawerTitle className="text-start">جستجو</DrawerTitle>
</DrawerHeader>
<div className="p-4">
<div className="flex items-center rounded-md border bg-background px-3 py-2">
<Search className="me-2 size-4 shrink-0 text-muted-foreground" />
<input
type="search"
placeholder="جستجوی فایل‌ها..."
aria-label="جستجوی فایل‌ها"
className="flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
/>
</div>
</div>
</DrawerContent>
</Drawer>
<Drawer
open={mobileDrawer === "explorer"}
onOpenChange={(open) => setMobileDrawer(open ? "explorer" : null)}
>
<DrawerContent dir="rtl" className="max-h-[85vh] text-start">
<DrawerHeader>
<DrawerTitle className="text-start">فایل‌ها</DrawerTitle>
</DrawerHeader>
<ScrollArea className="h-[60vh] [&>[data-slot=scroll-area-viewport]>div]:!block">
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>تغییرات</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{changesData.map((item, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuButton>
<File className="size-4" />
<span className="truncate">{item.file}</span>
</SidebarMenuButton>
<SidebarMenuBadge
className={cn(
"text-[10px] font-medium",
item.state === "M" && "text-yellow-500",
item.state === "U" && "text-green-500",
item.state === "D" && "text-red-500",
)}
>
{item.state}
</SidebarMenuBadge>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>فایل‌ها</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{fileTreeData.map((item, index) => (
<Tree key={index} item={item} />
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</ScrollArea>
</DrawerContent>
</Drawer>
<Drawer
open={mobileDrawer === "git"}
onOpenChange={(open) => setMobileDrawer(open ? "git" : null)}
>
<DrawerContent dir="rtl" className="max-h-[85vh] text-start">
<DrawerHeader>
<DrawerTitle className="text-start">کنترل منبع</DrawerTitle>
</DrawerHeader>
<ScrollArea className="h-[60vh] [&>[data-slot=scroll-area-viewport]>div]:!block">
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>تغییرات</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{changesData.map((item, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuButton>
<File className="size-4" />
<span className="truncate">{item.file}</span>
</SidebarMenuButton>
<SidebarMenuBadge
className={cn(
"text-[10px] font-medium",
item.state === "M" && "text-yellow-500",
item.state === "U" && "text-green-500",
item.state === "D" && "text-red-500",
)}
>
{item.state}
</SidebarMenuBadge>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</ScrollArea>
</DrawerContent>
</Drawer>
<Drawer
open={mobileDrawer === "chat"}
onOpenChange={(open) => setMobileDrawer(open ? "chat" : null)}
>
<DrawerContent dir="rtl" className="max-h-[85vh] text-start">
<DrawerHeader>
<DrawerTitle className="text-start">گفتگو</DrawerTitle>
</DrawerHeader>
<div className="flex h-[60vh] flex-col">
<ScrollArea className="flex-1 p-4">
<div className="flex justify-end">
<div className="max-w-[85%] rounded-2xl rounded-ee-sm bg-primary px-4 py-3 text-sm text-primary-foreground">
یک لندینگ برای ری یو آی با بخش قهرمان، شبکه
ویژگی‌ها و جدول قیمت‌گذاری بساز
</div>
</div>
</ScrollArea>
<div className="border-t p-3">
<div className="rounded-lg border bg-background">
<textarea
placeholder="توضیح دهید چه چیزی می‌خواهید بسازید..."
rows={3}
className="w-full resize-none bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
/>
<div className="flex items-center justify-between border-t px-3 py-2">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<span>اینتر برای ارسال</span>
</div>
<Button size="sm" className="h-7 gap-1.5 px-3">
<span>ارسال</span>
<ChevronRight className="size-3 rtl:rotate-180" />
</Button>
</div>
</div>
</div>
</div>
</DrawerContent>
</Drawer>
</div>
<div className="hidden min-w-0 flex-1 flex-col overflow-hidden md:flex">
<header className="flex h-12 shrink-0 items-center justify-between border-b bg-background px-4">
<nav className="flex items-center gap-1 text-sm">
<span className="font-medium">ری یو آی</span>
<ChevronRight className="size-4 text-muted-foreground rtl:rotate-180" />
<span className="text-muted-foreground">پروژه من</span>
</nav>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" className="h-8 gap-1.5">
<Play className="size-4" />
<span>پیش‌نمایش</span>
</Button>
<Button variant="ghost" size="icon" className="size-8">
<Plus className="size-4" />
</Button>
<Button variant="ghost" size="icon" className="size-8">
<History className="size-4" />
</Button>
<Button
variant="ghost"
size="icon"
className={cn("size-8", isChatOpen && "bg-muted")}
onClick={() => setIsChatOpen(!isChatOpen)}
>
<PanelRight className="size-4" />
</Button>
</div>
</header>
<div className="flex min-h-0 flex-1 overflow-hidden">
{renderSidebar()}
<SidebarInset className="min-h-0 flex-col overflow-hidden">
<div className="flex flex-1 overflow-hidden">
<div
className={cn(
"flex flex-1 items-center justify-center bg-muted/30",
)}
>
{!isChatOpen ? (
<div className="flex flex-col items-center gap-4 text-center">
<Logo className="size-16 opacity-30" />
<div className="space-y-2">
<h3 className="text-lg font-medium">شروع ساخت</h3>
<p className="max-w-sm text-sm text-muted-foreground">
توضیح دهید چه چیزی می‌خواهید بسازید تا هوش مصنوعی
کمک کند
</p>
</div>
<Button
onClick={() => setIsChatOpen(true)}
className="gap-2"
>
<MessageSquare className="size-4" />
باز کردن گفتگو
</Button>
</div>
) : (
<Logo className="size-16 opacity-30" />
)}
</div>
{isChatOpen && (
<div className="flex w-[400px] shrink-0 flex-col border-s">
<div className="flex h-10 items-center justify-between border-b px-4">
<div className="flex items-center gap-2">
<MessageSquare className="size-4" />
<span className="text-sm font-medium">گفتگو</span>
</div>
</div>
<ScrollArea className="flex-1 p-4">
<div className="flex justify-end">
<div className="max-w-[85%] rounded-2xl rounded-ee-sm bg-primary px-4 py-3 text-sm text-primary-foreground">
یک لندینگ برای ری یو آی با بخش قهرمان، شبکه
ویژگی‌ها و جدول قیمت‌گذاری بساز
</div>
</div>
</ScrollArea>
<div className="border-t p-3">
<div className="rounded-lg border bg-background">
<textarea
placeholder="توضیح دهید چه چیزی می‌خواهید بسازید..."
rows={3}
className="w-full resize-none bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground"
/>
<div className="flex items-center justify-between border-t px-3 py-2">
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<span>اینتر برای ارسال</span>
</div>
<Button size="sm" className="h-7 gap-1.5 px-3">
<span>ارسال</span>
<ChevronRight className="size-3 rtl:rotate-180" />
</Button>
</div>
</div>
</div>
</div>
)}
</div>
</SidebarInset>
</div>
</div>
</SidebarProvider>
);
}
+91
View File
@@ -0,0 +1,91 @@
import React from "react";
import { cn } from "@/lib/utils";
interface Awards1Props {
className?: string;
}
const faYear = (year: number) => year.toLocaleString("fa-IR");
const Awards1 = ({ className }: Awards1Props) => {
const awards = [
{
name: "برنده جوایز طراحی استایل‌دهی",
description:
"به پاس کیفیت ممتاز در طراحی و عملکرد وب مورد تقدیر قرار گرفته است.",
year: 2024,
url: "#",
},
{
name: "سایت روز Awwwards",
description:
"به دلیل نوآوری و خلاقیت در توسعه وب برگزیده شده است.",
year: 2023,
url: "#",
},
{
name: "بلاک‌های رابط ری یو آی",
description:
"به دلیل طراحی رابط کاربری و تجربه کاربری ممتاز مورد تقدیر قرار گرفته است.",
year: 2023,
url: "https://www.ShadcnBlocks",
},
{
name: "تعالی در طراحی وب",
description:
"به پاس کیفیت بالای طراحی و اجرای فنی مورد تقدیر قرار گرفته است.",
year: 2022,
url: "#",
},
];
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container space-y-10 lg:space-y-12">
<h1 className="max-w-md text-2xll tracking-tighter lg:text-2xll">
جوایز
</h1>
<table className="w-full border-collapse">
<thead>
<tr className="h-12 border-b text-start text-sm text-foreground/80">
<th className="font-semibold">جایزه</th>
<th className="font-semibold">توضیحات</th>
<th className="text-end font-semibold">سال</th>
</tr>
</thead>
<tbody>
{awards.map((award, index) => (
<tr
key={index}
className="h-20 border-b text-start text-sm text-foreground/40"
>
<td className="text-lg font-light tracking-tight text-foreground lg:text-xl">
<a
href={award.url}
target={award.url.startsWith("http") ? "_blank" : undefined}
rel={
award.url.startsWith("http")
? "noopener noreferrer"
: undefined
}
className="hover:underline"
title={award.name}
>
{award.name}
</a>
</td>
<td>{award.description}</td>
<td className="text-end text-foreground">
{faYear(award.year)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
);
};
export { Awards1 };
+50
View File
@@ -0,0 +1,50 @@
import React from "react";
import { cn } from "@/lib/utils";
interface Awards2Props {
className?: string;
}
const faYear = (year: number) => year.toLocaleString("fa-IR");
const Awards2 = ({ className }: Awards2Props) => {
const awards = [
{ title: "جایزه همکاری خلاقانه", year: 2023 },
{ title: "جایزه تعالی طراحی", year: 2023 },
{ title: "نوآوری در توسعه وب", year: 2022 },
{ title: "بهترین طراحی تجربه کاربری", year: 2022 },
{ title: "طراحی بصری برجسته", year: 2021 },
{ title: "جایزه نوآوری دیجیتال", year: 2021 },
{ title: "تعالی فناوری خلاقانه", year: 2020 },
{ title: "بهترین طراحی تعاملی", year: 2020 },
{ title: "دستاورد طراحی وب", year: 2019 },
];
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<div className="grid grid-cols-1 gap-20 lg:grid-cols-6">
<div className="top-10 col-span-2 flex h-fit w-fit items-center gap-3 py-8 lg:sticky">
<span className="size-3 bg-orange-500" />
<p className="text-xl text-foreground/30">جوایز</p>
</div>
<ul className="col-span-4 w-full">
{awards.map((award, index) => (
<li key={index} className="flex justify-between border-b py-4">
<h3 className="text-xl font-medium tracking-tight">
{award.title}
</h3>
<p className="font-mono text-lg tracking-tighter text-foreground/50">
{faYear(award.year)}
</p>
</li>
))}
</ul>
</div>
</div>
</section>
);
};
export { Awards2 };
+75
View File
@@ -0,0 +1,75 @@
import { MoveUpRight } from "lucide-react";
import { cn } from "@/lib/utils";
const faYear = (year: number) => year.toLocaleString("fa-IR");
const awards = [
{
title: "جایزه طراحی پیکسل‌محور",
categories: ["طراحی دیجیتال", "رابط و تجربه کاربری"],
year: 2023,
color: "bg-amber-300",
},
{
title: "جایزه برترین مهارت فنی",
categories: ["توسعه وب", "نوآوری"],
year: 2023,
color: "bg-red-500",
},
{
title: "جایزه برترین راهبرد",
categories: ["راهبرد محصول", "اثر بازار"],
year: 2022,
color: "bg-blue-500",
},
];
interface Awards3Props {
className?: string;
}
const Awards3 = ({ className }: Awards3Props) => {
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<h2 className="mb-6 text-xl font-semibold tracking-tighter text-foreground sm:text-xl lg:text-3xl">
جوایز و تقدیرها
</h2>
<p className="mb-6 max-w-3xl text-base text-muted-foreground">
تعهد ما به کیفیت، نوآوری و خلق تجربه‌های ارزشمند، زمینه‌ساز دریافت این افتخارات بوده است. این دستاوردها انگیزه‌ای برای ادامه مسیر و ارائه راهکارهای بهتر هستند.
</p>
{awards.map((award, index) => (
<div
key={index}
className="group relative flex items-start gap-4 overflow-hidden border-b border-border p-4"
>
<div className="absolute inset-0 z-0 translate-y-full bg-accent transition-transform duration-300 ease-in-out group-hover:translate-y-0" />
<div className="relative z-10 flex-1 space-y-2 pb-4">
<div className="flex items-center gap-4">
<div
className={`h-5 w-5 rounded-md ${award.color} flex items-center justify-center`}
></div>
<h3 className="rounded-md font-medium">{award.title}</h3>
</div>
<p className="flex gap-x-2 ps-9 text-sm text-muted-foreground">
{award.categories.map((category, index) => (
<span key={index}>{category}</span>
))}
</p>
<p className="ps-9 text-sm text-muted-foreground">
{faYear(award.year)}
</p>
</div>
<MoveUpRight className="relative z-10 mt-1 h-4 w-4 text-muted-foreground rtl:-scale-x-100" />
</div>
))}
</div>
</section>
);
};
export { Awards3 };
+115
View File
@@ -0,0 +1,115 @@
import React from "react";
import { cn } from "@/lib/utils";
interface Awards4Props {
className?: string;
}
const faYear = (year: number) => year.toLocaleString("fa-IR");
const faRowNum = (index: number) =>
`(${(index + 1).toLocaleString("fa-IR", { minimumIntegerDigits: 3 })})`;
const Awards4 = ({ className }: Awards4Props) => {
const awards = [
{
name: "برنده جوایز طراحی استایل‌دهی",
description:
"به پاس کیفیت ممتاز در طراحی و عملکرد وب مورد تقدیر قرار گرفته است.",
year: 2024,
logo: "/block-media/shadcnblocks/block/block-1.svg",
},
{
name: "سایت روز Awwwards",
description:
"به دلیل نوآوری و خلاقیت در توسعه وب برگزیده شده است.",
year: 2023,
logo: "/block-media/shadcnblocks/block/block-2.svg",
},
{
name: "بهترین طراحی رابط و تجربه کاربری",
description:
"به دلیل طراحی رابط کاربری و تجربه کاربری ممتاز مورد تقدیر قرار گرفته است.",
year: 2023,
logo: "/block-media/shadcnblocks/block/block-3.svg",
},
{
name: "تعالی در طراحی وب",
description:
"به پاس کیفیت بالای طراحی و اجرای فنی مورد تقدیر قرار گرفته است.",
year: 2022,
logo: "/block-media/shadcnblocks/block/block-4.svg",
},
{
name: "اپلیکیشن موبایل سال",
description:
"به دلیل نوآوری در طراحی موبایل و ارائه تجربه کاربری برتر مورد تقدیر قرار گرفته است.",
year: 2022,
logo: "/block-media/shadcnblocks/block/block-5.svg",
},
{
name: "جایزه نوآوری دیجیتال",
description:
"به دلیل ارائه راهکارهای دیجیتال نوآورانه و فناوری خلاقانه مورد تقدیر قرار گرفته است.",
year: 2021,
logo: "/block-media/shadcnblocks/block/block-6.svg",
},
];
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container space-y-10 lg:space-y-20">
<div className="flex w-full items-end justify-between">
<h1 className="text-2xll font-semibold tracking-tighter lg:text-8xl">
جوایز
</h1>
<h2 className="text-lg font-semibold tracking-tighter lg:text-3xl">
(۲۱۵–۲۲۵©)
</h2>
</div>
<table className="w-full border-collapse">
<thead>
<tr className="h-12 border-b text-start text-foreground/40">
<th className="hidden min-w-30 font-normal lg:table-cell lg:min-w-40"></th>
<th className="font-normal">نام</th>
<th className="hidden font-normal md:table-cell">نامزدی</th>
<th className="text-end font-normal">سال</th>
</tr>
</thead>
<tbody>
{awards.map((award, index) => (
<tr
key={index}
className="h-20 border-b text-start text-foreground/40"
>
<td className="hidden font-medium tracking-tight text-foreground/50 lg:table-cell">
{faRowNum(index)}
</td>
<td className="text-lg font-medium tracking-tight text-foreground lg:text-xl">
<div className="flex items-center gap-2">
<span className="size-8 rounded-xl bg-foreground object-cover p-1.5">
<img
src={award.logo}
className="invert"
alt={award.name}
/>
</span>
{award.name}
</div>
</td>
<td className="hidden md:table-cell">{award.description}</td>
<td className="text-end text-foreground">
{faYear(award.year)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
);
};
export { Awards4 };
+132
View File
@@ -0,0 +1,132 @@
"use client";
import { motion, useSpring } from "framer-motion";
import React, { useState } from "react";
import { cn } from "@/lib/utils";
interface Awards5Props {
className?: string;
}
const faOrder = (index: number) => (index + 1).toLocaleString("fa-IR");
const Awards5 = ({ className }: Awards5Props) => {
const SPRING = {
mass: 0.1,
stiffness: 100,
damping: 10,
};
const xSpring = useSpring(0, SPRING);
const ySpring = useSpring(0, SPRING);
const scaleSpring = useSpring(0, SPRING);
const opacity = useSpring(0, SPRING);
const awards = [
{
title: "جایزه همکاری خلاقانه",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img1.png",
},
{
title: "جایزه تعالی طراحی",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img2.png",
},
{
title: "نوآوری در توسعه وب",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img3.png",
},
{
title: "بهترین طراحی تجربه کاربری",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img4.png",
},
{
title: "طراحی بصری برجسته",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img5.png",
},
{
title: "جایزه نوآوری دیجیتال",
url: "#",
image: "/block-media/shadcnblocks/block/guri4/img6.png",
},
];
const handlePointerMove = (e: React.PointerEvent<HTMLUListElement>) => {
const bounds = e.currentTarget.getBoundingClientRect();
xSpring.set(e.clientX - bounds.left + 20);
ySpring.set(e.clientY - bounds.top + 20);
};
const [currentIndex, setCurrentIndex] = useState(0);
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="font-hedvigLettersSerif container">
<div className="grid grid-cols-1 gap-5 lg:grid-cols-6 lg:gap-20">
<div className="top-10 col-span-2 flex h-fit w-fit items-center gap-3 py-8 lg:sticky">
<p className="text-xl text-foreground/30">
{Number(5).toLocaleString("fa-IR")} جوایز
</p>
</div>
<ul
onPointerMove={handlePointerMove}
onPointerEnter={() => {
opacity.set(1);
scaleSpring.set(1);
}}
onPointerLeave={() => {
opacity.set(0);
scaleSpring.set(0);
}}
className="relative col-span-4 w-full"
>
{awards.map((award, index) => (
<li
key={index}
onPointerEnter={() => {
setCurrentIndex(index);
}}
className="group relative flex cursor-pointer items-center justify-between border-b py-5"
>
<h3 className="text-xl font-medium tracking-tight lg:text-2xll">
{award.title}
<sup className="align-top text-xs lg:text-sm">
{faOrder(index)}
</sup>
</h3>
<a
href={award.url}
className="text-sm tracking-tighter text-foreground/30 hover:text-foreground"
>
مشاهده جایزه
</a>
</li>
))}
<motion.div
className="pointer-events-none absolute top-0 inset-s-0 size-40 border"
style={{
x: xSpring,
y: ySpring,
opacity: opacity,
scale: scaleSpring,
}}
>
<img
src={awards[currentIndex].image}
alt={awards[currentIndex].title}
className="h-full w-full object-cover"
/>
</motion.div>
</ul>
</div>
</div>
</section>
);
};
export { Awards5 };
+25
View File
@@ -0,0 +1,25 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern1Props {
className?: string;
}
const BackgroundPattern1 = ({ className }: BackgroundPattern1Props) => {
return (
<section className={cn("relative min-h-screen w-full", className)}>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Top Primary Radial Background Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(125% 125% at 50% 10%, var(--background) 40%, var(--primary) 100%)",
}}
/>
</section>
);
};
export { BackgroundPattern1 };
+32
View File
@@ -0,0 +1,32 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern10Props {
className?: string;
}
const BackgroundPattern10 = ({ className }: BackgroundPattern10Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Basic Center Grid (corner hide) Background */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:40px_40px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
maskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern10 };
+68
View File
@@ -0,0 +1,68 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern100Props {
className?: string;
}
const BackgroundPattern100 = ({ className }: BackgroundPattern100Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Gradient Blend with Noise */}
<div
className="absolute inset-0 z-0"
style={{
background: `
radial-gradient(circle 900px at 0% 100%,
oklch(from var(--chart-1) calc(l * 1.1) calc(c * 1.8) h / 0.8) 0%,
oklch(from var(--chart-1) calc(l * 0.9) calc(c * 1.6) h / 0.6) 25%,
oklch(from var(--chart-1) calc(l * 0.8) calc(c * 1.4) h / 0.3) 50%,
transparent 80%
),
radial-gradient(circle 900px at 100% 100%,
oklch(from var(--chart-2) calc(l * 1.1) calc(c * 1.8) h / 0.8) 0%,
oklch(from var(--chart-2) calc(l * 0.9) calc(c * 1.6) h / 0.6) 25%,
oklch(from var(--chart-2) calc(l * 0.8) calc(c * 1.4) h / 0.3) 50%,
transparent 80%
),
var(--background)
`,
filter: "blur(120px)",
}}
/>
<div className="pointer-events-none absolute inset-0 z-10">
<svg
className="h-full w-full opacity-30"
style={{ mixBlendMode: "overlay" }}
>
<filter id="noise-pattern">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
seed="2"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
<rect
width="100%"
height="100%"
filter="url(#noise-pattern)"
fill="white"
/>
</svg>
</div>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern100 };
+32
View File
@@ -0,0 +1,32 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern11Props {
className?: string;
}
const BackgroundPattern11 = ({ className }: BackgroundPattern11Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern11 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern111Props {
className?: string;
}
const BackgroundPattern111 = ({ className }: BackgroundPattern111Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with left fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse at 80% 14%, rgba(0, 0, 0, 1), transparent 40%)",
WebkitMaskImage:
"radial-gradient(ellipse at 80% 14%, rgba(0, 0, 0, 1), transparent 40%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern111 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern112Props {
className?: string;
}
const BackgroundPattern112 = ({ className }: BackgroundPattern112Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with right fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse at 20% 14%, rgba(0, 0, 0, 1), transparent 40%)",
WebkitMaskImage:
"radial-gradient(ellipse at 20% 14%, rgba(0, 0, 0, 1), transparent 40%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern112 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern113Props {
className?: string;
}
const BackgroundPattern113 = ({ className }: BackgroundPattern113Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with bottom right fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse at 100% 100%, rgba(0, 0, 0, 1), transparent 40%)",
WebkitMaskImage:
"radial-gradient(ellipse at 100% 100%, rgba(0, 0, 0, 1), transparent 40%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern113 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern115Props {
className?: string;
}
const BackgroundPattern115 = ({ className }: BackgroundPattern115Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with Center fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 1), transparent 60%)",
WebkitMaskImage:
"radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 1), transparent 60%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern115 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern116Props {
className?: string;
}
const BackgroundPattern116 = ({ className }: BackgroundPattern116Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with top center fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse 95% 54% at 50% 0%, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.4) 34%, transparent 74%)",
WebkitMaskImage:
"radial-gradient(ellipse 95% 54% at 50% 0%, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.4) 34%, transparent 74%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern116 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern117Props {
className?: string;
}
const BackgroundPattern117 = ({ className }: BackgroundPattern117Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* radial gradient with Bottom center fade dot Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(oklch(from var(--primary) calc(l * 0.8) calc(c * 1.5) h / 1) 1px, transparent 1px)",
backgroundSize: "3px 3px",
maskImage:
"radial-gradient(ellipse 95% 54% at 50% 100%, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.4) 34%, transparent 74%)",
WebkitMaskImage:
"radial-gradient(ellipse 95% 54% at 50% 100%, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.4) 34%, transparent 74%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern117 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern12Props {
className?: string;
}
const BackgroundPattern12 = ({ className }: BackgroundPattern12Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Right Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern12 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern13Props {
className?: string;
}
const BackgroundPattern13 = ({ className }: BackgroundPattern13Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Left Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern13 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern14Props {
className?: string;
}
const BackgroundPattern14 = ({ className }: BackgroundPattern14Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Top Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
maskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern14 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern15Props {
className?: string;
}
const BackgroundPattern15 = ({ className }: BackgroundPattern15Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern15 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern16Props {
className?: string;
}
const BackgroundPattern16 = ({ className }: BackgroundPattern16Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Right Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern16 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern17Props {
className?: string;
}
const BackgroundPattern17 = ({ className }: BackgroundPattern17Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Left Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern17 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern18Props {
className?: string;
}
const BackgroundPattern18 = ({ className }: BackgroundPattern18Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Center Fade Diagonal Cross Grid */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-linear-gradient(45deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px),
repeating-linear-gradient(135deg, transparent, transparent 32px, var(--muted) 32px, var(--muted) 33px)
`,
WebkitMaskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
maskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern18 };
+68
View File
@@ -0,0 +1,68 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern19Props {
className?: string;
}
const BackgroundPattern19 = ({ className }: BackgroundPattern19Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern19 };
+25
View File
@@ -0,0 +1,25 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern2Props {
className?: string;
}
const BackgroundPattern2 = ({ className }: BackgroundPattern2Props) => {
return (
<section className={cn("relative min-h-screen w-full", className)}>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Bottom Primary Radial Background Pattern */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(125% 125% at 50% 90%, var(--background) 40%, var(--primary) 100%)",
}}
/>
</section>
);
};
export { BackgroundPattern2 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern20Props {
className?: string;
}
const BackgroundPattern20 = ({ className }: BackgroundPattern20Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Right Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern20 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern21Props {
className?: string;
}
const BackgroundPattern21 = ({ className }: BackgroundPattern21Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Left Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern21 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern22Props {
className?: string;
}
const BackgroundPattern22 = ({ className }: BackgroundPattern22Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Top Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern22 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern23Props {
className?: string;
}
const BackgroundPattern23 = ({ className }: BackgroundPattern23Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern23 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern24Props {
className?: string;
}
const BackgroundPattern24 = ({ className }: BackgroundPattern24Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Right Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern24 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern25Props {
className?: string;
}
const BackgroundPattern25 = ({ className }: BackgroundPattern25Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Left Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern25 };
+70
View File
@@ -0,0 +1,70 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern26Props {
className?: string;
}
const BackgroundPattern26 = ({ className }: BackgroundPattern26Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Center Fade Dashed Grid Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "20px 20px",
backgroundPosition: "0 0, 0 0",
maskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)
`,
WebkitMaskImage: `
repeating-linear-gradient(
to right,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
repeating-linear-gradient(
to bottom,
black 0px,
black 3px,
transparent 3px,
transparent 8px
),
radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)
`,
maskComposite: "intersect",
WebkitMaskComposite: "source-in",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern26 };
+30
View File
@@ -0,0 +1,30 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern27Props {
className?: string;
}
const BackgroundPattern27 = ({ className }: BackgroundPattern27Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Gradinet Background with Top Glow */}
<div
className="absolute inset-0 z-0"
style={{
background:
"radial-gradient(ellipse 80% 60% at 50% 0%, oklch(from var(--primary) calc(l - 0.1) c h / 0.20), transparent 70%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern27 };
+37
View File
@@ -0,0 +1,37 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern28Props {
className?: string;
}
const BackgroundPattern28 = ({ className }: BackgroundPattern28Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Center Spotlight Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
background: `
radial-gradient(
circle at center,
oklch(from var(--primary) calc(l * 0.85) calc(c * 0.8) h / 0.12) 0%,
oklch(from var(--primary) calc(l * 0.8) calc(c * 0.75) h / 0.08) 30%,
oklch(from var(--primary) calc(l * 0.75) calc(c * 0.7) h / 0.04) 60%,
transparent 80%
)
`,
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern28 };
+29
View File
@@ -0,0 +1,29 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern29Props {
className?: string;
}
const BackgroundPattern29 = ({ className }: BackgroundPattern29Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Top Radial Glow Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `radial-gradient(circle 500px at 50% 100px, oklch(from var(--primary) calc(l * 0.85) calc(c * 0.8) h / 0.2), oklch(from var(--primary) calc(l * 0.7) calc(c * 0.6) h / 0.1) 60%, transparent)`,
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern29 };
+24
View File
@@ -0,0 +1,24 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern3Props {
className?: string;
}
const BackgroundPattern3 = ({ className }: BackgroundPattern3Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Basic Grid Background */}
<div className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:40px_40px]" />
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern3 };
+33
View File
@@ -0,0 +1,33 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern30Props {
className?: string;
}
const BackgroundPattern30 = ({ className }: BackgroundPattern30Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Corner Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
radial-gradient(circle 600px at 0% 200px, oklch(from var(--primary) calc(l * 0.7) calc(c * 0.6) h / 0.15), transparent),
radial-gradient(circle 600px at 100% 200px, oklch(from var(--primary) calc(l * 0.75) calc(c * 0.65) h / 0.12), transparent)
`,
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern30 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern31Props {
className?: string;
}
const BackgroundPattern31 = ({ className }: BackgroundPattern31Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern31 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern32Props {
className?: string;
}
const BackgroundPattern32 = ({ className }: BackgroundPattern32Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Right Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern32 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern33Props {
className?: string;
}
const BackgroundPattern33 = ({ className }: BackgroundPattern33Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Left Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern33 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern34Props {
className?: string;
}
const BackgroundPattern34 = ({ className }: BackgroundPattern34Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Top Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
maskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern34 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern35Props {
className?: string;
}
const BackgroundPattern35 = ({ className }: BackgroundPattern35Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern35 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern36Props {
className?: string;
}
const BackgroundPattern36 = ({ className }: BackgroundPattern36Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Right Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern36 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern37Props {
className?: string;
}
const BackgroundPattern37 = ({ className }: BackgroundPattern37Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Left Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern37 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern38Props {
className?: string;
}
const BackgroundPattern38 = ({ className }: BackgroundPattern38Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Center Fade Circuit Board Background */}
<div
className="pointer-events-none absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px),
linear-gradient(90deg, var(--muted) 1px, transparent 1px),
linear-gradient(180deg, var(--muted) 1px, transparent 1px)
`,
backgroundSize: "50px 50px, 50px 50px, 10px 10px, 10px 10px",
WebkitMaskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
maskImage:
"radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 70%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern38 };
+36
View File
@@ -0,0 +1,36 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern39Props {
className?: string;
}
const BackgroundPattern39 = ({ className }: BackgroundPattern39Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* White Grid with Dots Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px),
radial-gradient(circle, oklch(from var(--foreground) l c h / 0.1) 1px, transparent 1px)
`,
backgroundSize: "20px 20px, 20px 20px, 20px 20px",
backgroundPosition: "0 0, 0 0, 0 0",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern39 };
+27
View File
@@ -0,0 +1,27 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern4Props {
className?: string;
}
const BackgroundPattern4 = ({ className }: BackgroundPattern4Props) => {
return (
<section className={cn("relative min-h-screen w-full", className)}>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Diagonal Fade Grid Left Pattern */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
}}
/>
</section>
);
};
export { BackgroundPattern4 };
+40
View File
@@ -0,0 +1,40 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern40Props {
className?: string;
}
const BackgroundPattern40 = ({ className }: BackgroundPattern40Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Right Fade White Grid with Dots Background */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
linear-gradient(to right, var(--muted) 1px, transparent 1px),
linear-gradient(to bottom, var(--muted) 1px, transparent 1px),
radial-gradient(circle, oklch(from var(--foreground) l c h / 0.1) 1px, transparent 1px)
`,
backgroundSize: "20px 20px, 20px 20px, 20px 20px",
backgroundPosition: "0 0, 0 0, 0 0",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 0%, #000 50%, transparent 90%)",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern40 };
+27
View File
@@ -0,0 +1,27 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern5Props {
className?: string;
}
const BackgroundPattern5 = ({ className }: BackgroundPattern5Props) => {
return (
<section className={cn("relative min-h-screen w-full", className)}>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Diagonal Fade Grid Right Pattern*/}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 0%, #000 50%, transparent 90%)",
}}
/>
</section>
);
};
export { BackgroundPattern5 };
+27
View File
@@ -0,0 +1,27 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern6Props {
className?: string;
}
const BackgroundPattern6 = ({ className }: BackgroundPattern6Props) => {
return (
<section className={cn("relative min-h-screen w-full", className)}>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Top Fade Grid Pattern */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
maskImage:
"radial-gradient(ellipse 70% 60% at 50% 0%, #000 60%, transparent 100%)",
}}
/>
</section>
);
};
export { BackgroundPattern6 };
+32
View File
@@ -0,0 +1,32 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern7Props {
className?: string;
}
const BackgroundPattern7 = ({ className }: BackgroundPattern7Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Bottom Fade Grid Pattern */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 100% 80% at 50% 100%, #000 50%, transparent 90%)",
}}
/>
</section>
);
};
export { BackgroundPattern7 };
+32
View File
@@ -0,0 +1,32 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern8Props {
className?: string;
}
const BackgroundPattern8 = ({ className }: BackgroundPattern8Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Bottom Left Fade Grid Pattern */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 0% 100%, #000 50%, transparent 90%)",
}}
/>
</section>
);
};
export { BackgroundPattern8 };
+32
View File
@@ -0,0 +1,32 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern9Props {
className?: string;
}
const BackgroundPattern9 = ({ className }: BackgroundPattern9Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
<PatternPlaceholder />
{/* Background Pattern */}
{/* Bottom Right Fade Grid Pattern */}
<div
className="absolute inset-0 z-0 bg-[linear-gradient(to_right,_var(--muted)_1px,_transparent_1px),linear-gradient(to_bottom,_var(--muted)_1px,_transparent_1px)] bg-[length:32px_32px]"
style={{
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
maskImage:
"radial-gradient(ellipse 80% 80% at 100% 100%, #000 50%, transparent 90%)",
}}
/>
</section>
);
};
export { BackgroundPattern9 };
+55
View File
@@ -0,0 +1,55 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern95Props {
className?: string;
}
const BackgroundPattern95 = ({ className }: BackgroundPattern95Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Multi-Corner Radial Gradient Glow */}
<div
className="absolute inset-0 z-0"
style={{
background: `
radial-gradient(at 34% 18%, oklch(from var(--chart-1) l c h / 0.15) 0%, transparent 50%),
radial-gradient(at 90% 64%, oklch(from var(--chart-2) l c h / 0.15) 0%, transparent 50%),
radial-gradient(at 69% 59%, oklch(from var(--chart-3) l c h / 0.15) 0%, transparent 50%),
radial-gradient(at 0% 78%, oklch(from var(--chart-4) l c h / 0.15) 0%, transparent 50%),
var(--background)
`,
}}
/>
{/* Noise Texture Layer */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
repeating-conic-gradient(from 0deg at 50% 50%,
oklch(from var(--muted) l c h / 0.03) 0deg 0.5deg,
transparent 0.5deg 1deg
),
repeating-conic-gradient(from 45deg at 50% 50%,
oklch(from var(--foreground) l c h / 0.50) 0deg 0.5deg,
transparent 0.5deg 1deg
)
`,
backgroundSize: "5px 5px, 5px 5px",
mixBlendMode: "overlay",
}}
/>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern95 };
+59
View File
@@ -0,0 +1,59 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern96Props {
className?: string;
}
const BackgroundPattern96 = ({ className }: BackgroundPattern96Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Gradient Glow with Noise Texture */}
<div
className="absolute inset-0 z-0"
style={{
background: `
radial-gradient(at 0% 0%, oklch(from var(--chart-1) l c h / 0.25) 0%, transparent 40%),
radial-gradient(at 10% 10%, oklch(from var(--chart-2) l c h / 0.2) 0%, transparent 45%),
radial-gradient(at 5% 15%, oklch(from var(--chart-3) l c h / 0.18) 0%, transparent 50%),
radial-gradient(at 15% 5%, oklch(from var(--chart-4) l c h / 0.15) 0%, transparent 35%),
var(--background)
`,
}}
/>
<div className="pointer-events-none absolute inset-0 z-10">
<svg
className="h-full w-full opacity-20"
style={{ mixBlendMode: "overlay" }}
>
<filter id="noise-pattern">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
seed="2"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
<rect
width="100%"
height="100%"
filter="url(#noise-pattern)"
fill="white"
/>
</svg>
</div>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern96 };
+60
View File
@@ -0,0 +1,60 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern97Props {
className?: string;
}
const BackgroundPattern97 = ({ className }: BackgroundPattern97Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Mesh Glow with Noise Texture */}
<div
className="absolute inset-0 z-0"
style={{
background: `
radial-gradient(circle at 0% 0%, oklch(from var(--chart-1) calc(l * 1.2) calc(c * 1.5) h / 0.4) 0%, transparent 30%),
radial-gradient(circle at 100% 100%, oklch(from var(--chart-2) calc(l * 1.1) calc(c * 1.6) h / 0.35) 0%, transparent 35%),
radial-gradient(circle at 50% 50%, oklch(from var(--chart-3) calc(l * 0.9) calc(c * 1.4) h / 0.25) 0%, transparent 50%),
radial-gradient(ellipse 120% 100% at 30% 70%, oklch(from var(--chart-4) calc(l * 1.1) calc(c * 1.5) h / 0.3) 0%, transparent 40%),
var(--background)
`,
filter: "blur(100px)",
}}
/>
<div className="pointer-events-none absolute inset-0 z-10">
<svg
className="h-full w-full opacity-20"
style={{ mixBlendMode: "overlay" }}
>
<filter id="noise-pattern">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
seed="2"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
<rect
width="100%"
height="100%"
filter="url(#noise-pattern)"
fill="white"
/>
</svg>
</div>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern97 };
+58
View File
@@ -0,0 +1,58 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern98Props {
className?: string;
}
const BackgroundPattern98 = ({ className }: BackgroundPattern98Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Bottom Glow with Noise Texture */}
<div
className="absolute inset-0 z-0"
style={{
backgroundImage: `
radial-gradient(circle at 50% 100%, oklch(from var(--chart-1) l calc(c * 1.8) h / 0.6) 0%, transparent 60%),
radial-gradient(circle at 50% 100%, oklch(from var(--chart-2) l calc(c * 1.5) h / 0.4) 0%, transparent 70%),
radial-gradient(circle at 50% 100%, oklch(from var(--chart-3) l calc(c * 1.3) h / 0.3) 0%, transparent 80%)
`,
filter: "blur(100px)",
}}
/>
<div className="pointer-events-none absolute inset-0 z-10">
<svg
className="h-full w-full opacity-30"
style={{ mixBlendMode: "overlay" }}
>
<filter id="noise-pattern">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
seed="2"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
<rect
width="100%"
height="100%"
filter="url(#noise-pattern)"
fill="white"
/>
</svg>
</div>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern98 };
+58
View File
@@ -0,0 +1,58 @@
import { PatternPlaceholder } from "@/components/shadcnblocks/pattern-placeholder";
import { cn } from "@/lib/utils";
interface BackgroundPattern99Props {
className?: string;
}
const BackgroundPattern99 = ({ className }: BackgroundPattern99Props) => {
return (
<section
className={cn(
"relative flex min-h-screen w-full items-center justify-center",
className,
)}
>
{/* Background Pattern */}
{/* Duo-Color Gradient Blend with Noise */}
<div
className="absolute inset-0 z-0"
style={{
background: `
radial-gradient(circle at 100% 0%, oklch(from var(--chart-1) calc(l * 1.2) calc(c * 1.5) h / 0.6) 0%, transparent 45%),
radial-gradient(circle at 0% 100%, oklch(from var(--chart-2) calc(l * 1.2) calc(c * 1.4) h / 0.4) 0%, transparent 50%),
var(--background)
`,
filter: "blur(120px)",
}}
/>
<div className="pointer-events-none absolute inset-0 z-10">
<svg
className="h-full w-full opacity-30"
style={{ mixBlendMode: "overlay" }}
>
<filter id="noise-pattern">
<feTurbulence
type="fractalNoise"
baseFrequency="0.8"
numOctaves="4"
stitchTiles="stitch"
seed="2"
/>
<feColorMatrix type="saturate" values="0" />
</filter>
<rect
width="100%"
height="100%"
filter="url(#noise-pattern)"
fill="white"
/>
</svg>
</div>
<PatternPlaceholder />
</section>
);
};
export { BackgroundPattern99 };
+74
View File
@@ -0,0 +1,74 @@
"use client";
import { X } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface Banner1Props {
title: string;
description: string;
linkText: string;
linkUrl: string;
defaultVisible?: boolean;
className?: string;
}
const Banner1 = ({
title = "نسخه ۲۰ منتشر شد!",
description = "جزئیات این نسخه را",
linkText = "مشاهده کنید",
linkUrl = "#",
defaultVisible = true,
className,
}: Banner1Props) => {
const [isVisible, setIsVisible] = useState(defaultVisible);
const handleClose = () => {
setIsVisible(false);
};
if (!isVisible) return null;
return (
<section
dir="rtl"
lang="fa"
className={cn("w-full border-b bg-background px-4 py-3", className)}
>
<div className="flex items-center justify-between gap-2">
<div className="flex-1 text-center">
<span className="text-sm">
<span className="font-medium">{title}</span>{" "}
<span className="text-muted-foreground">
{description}{" "}
<a
href={linkUrl}
className="underline underline-offset-2 hover:text-foreground"
target="_blank"
rel="noopener noreferrer"
>
{linkText}
</a>
.
</span>
</span>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="-ms-2 h-8 w-8 flex-none"
aria-label="بستن"
onClick={handleClose}
>
<X className="h-4 w-4" />
</Button>
</div>
</section>
);
};
export { Banner1 };
+76
View File
@@ -0,0 +1,76 @@
"use client";
import { X } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
interface Banner2Props {
title: string;
description: string;
linkText: string;
linkUrl: string;
defaultVisible?: boolean;
className?: string;
}
const Banner2 = ({
title = "نسخه ۲۰ منتشر شد!",
description = "جزئیات این نسخه را",
linkText = "مشاهده کنید",
linkUrl = "#",
defaultVisible = true,
className,
}: Banner2Props) => {
const [isVisible, setIsVisible] = useState(defaultVisible);
const handleClose = () => {
setIsVisible(false);
};
if (!isVisible) return null;
return (
<section
dir="rtl"
lang="fa"
className={cn("w-full bg-muted px-4 py-3", className)}
>
<div className="container">
<div className="flex items-center gap-2">
<div className="flex-1 text-start">
<span className="text-sm">
<span className="font-medium">{title}</span>{" "}
<span className="text-muted-foreground">
{description}{" "}
<a
href={linkUrl}
className="underline underline-offset-2 hover:text-foreground"
target="_blank"
rel="noopener noreferrer"
>
{linkText}
</a>
.
</span>
</span>
</div>
<Button
type="button"
variant="ghost"
size="icon"
className="-ms-2 h-8 w-8 flex-none"
aria-label="بستن"
onClick={handleClose}
>
<X className="h-4 w-4" />
</Button>
</div>
</div>
</section>
);
};
export { Banner2 };

Some files were not shown because too many files have changed in this diff Show More