Initial import of 1407 block sources under blocks/
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -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
|
||||
```
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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">
|
||||
“برند و محصول ما را همراستا کردند و اثر آن فوراً در
|
||||
تیمها و مشتریان ما دیده شد.”
|
||||
</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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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="بستن">
|
||||
×
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 };
|
||||
@@ -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
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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 };
|
||||
@@ -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
Reference in New Issue
Block a user