Files
reui-blocks/blocks/application-shell14.tsx
T

611 lines
20 KiB
TypeScript
Raw Normal View History

"use client";
import {
ArrowDownRight,
ArrowLeftRight,
ArrowUpRight,
BarChart3,
Bell,
ChevronDown,
Copy,
Gift,
HelpCircle,
LayoutGrid,
LogOut,
type LucideIcon,
Menu,
Moon,
Search,
Settings,
Star,
TrendingUp,
Wallet,
} from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import { cn } from "@/lib/utils";
type NavLink = {
label: string;
href: string;
isActive?: boolean;
icon: LucideIcon;
};
type TokenItem = {
name: string;
symbol: string;
color: string;
};
type NotificationItem = {
title: string;
detail: string;
time: string;
tone: string;
};
const data = {
brand: {
name: "NexoChain",
},
user: {
name: "علی رضایی",
email: "ali@example.com",
avatar:
"/block-media/shadcnblocks/block/avatar-2.webp",
},
wallet: {
address: "0x1cf2...9a56",
balance: "$460,890.02",
status: "متصل",
},
};
const navLinks: NavLink[] = [
{ label: "نمای کلی", href: "#", isActive: true, icon: LayoutGrid },
{ label: "بازارها", href: "#", icon: BarChart3 },
{ label: "معامله", href: "#", icon: ArrowLeftRight },
{ label: "پاداش‌ها", href: "#", icon: TrendingUp },
];
const favorites: TokenItem[] = [
{ name: "Bitcoin", symbol: "BTC", color: "bg-orange-500" },
{ name: "Ethereum", symbol: "ETH", color: "bg-indigo-500" },
{ name: "XRP", symbol: "XRP", color: "bg-neutral-900" },
{ name: "TetherUS", symbol: "IRRT", color: "bg-emerald-500" },
{ name: "Tron", symbol: "TRX", color: "bg-red-500" },
{ name: "Solana", symbol: "SOL", color: "bg-violet-500" },
{ name: "IRRC", symbol: "IRRC", color: "bg-blue-500" },
{ name: "BNB", symbol: "BNB", color: "bg-yellow-500" },
{ name: "Avalanche", symbol: "AVAX", color: "bg-rose-500" },
{ name: "Cardano", symbol: "ADA", color: "bg-sky-500" },
];
const walletAssets: TokenItem[] = [
{ name: "Bitcoin", symbol: "BTC", color: "bg-orange-500" },
{ name: "Ethereum", symbol: "ETH", color: "bg-indigo-500" },
{ name: "XRP", symbol: "XRP", color: "bg-neutral-900" },
{ name: "TetherUS", symbol: "IRRT", color: "bg-emerald-500" },
];
const notifications: NotificationItem[] = [
{
title: "برای امنیت حساب، احراز دو مرحله‌ای را فعال کنید",
detail: "کروم روی macOS، استانبول",
time: "۵ دقیقه",
tone: "text-rose-500",
},
{
title: "مبادله انجام شد",
detail: "۰٫۵ BTC - ۱۲٬۳۵۰ IRRC",
time: "اکنون",
tone: "text-emerald-500",
},
{
title: "۱۵ IRRC پاداش معاملاتی دریافت کردید",
detail: "در انتظار تأیید بلاک",
time: "۱ ساعت",
tone: "text-emerald-500",
},
{
title: "ورود جدید",
detail: "کروم روی macOS، استانبول",
time: "۱۰ دقیقه",
tone: "text-orange-500",
},
{
title: "تعمیرات برنامه‌ریزی‌شده",
detail: "فردا ساعت ۰۳:۰۰ UTC",
time: "دیروز",
tone: "text-muted-foreground",
},
];
function TokenBadge({ item }: { item: TokenItem }) {
return (
<span
className={cn(
"flex size-6 items-center justify-center rounded-full text-[11px] font-semibold text-white",
item.color,
)}
>
{item.symbol.slice(0, 1)}
</span>
);
}
function HeaderTab({ item }: { item: NavLink }) {
return (
<a
href={item.href}
className={cn(
"rounded-full px-3 py-1 text-sm",
item.isActive
? "bg-accent text-foreground"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
)}
>
{item.label}
</a>
);
}
function SearchField({ className }: { className?: string }) {
return (
<div className={cn("relative", className)}>
<Search className="pointer-events-none absolute top-1/2 start-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="جستجوی توکن یا جفت..."
className="h-9 w-full rounded-full bg-muted ps-9"
aria-label="جستجوی توکن"
/>
<span className="pointer-events-none absolute top-1/2 end-2.5 -translate-y-1/2 rounded-full bg-accent px-2 py-0.5 text-[10px] text-muted-foreground">
/
</span>
</div>
);
}
function WalletMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="gap-2 rounded-full border border-border bg-background px-3"
>
<span className="flex size-5 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-3.5" />
</span>
<span className="text-sm font-medium text-foreground">
{data.wallet.address}
</span>
<ChevronDown className="size-3 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-2">
<span className="flex size-9 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-4" />
</span>
<div>
<div className="text-sm font-medium text-foreground">
{data.wallet.address}
</div>
<div className="text-xs text-primary">{data.wallet.status}</div>
</div>
</div>
<Button variant="outline" size="sm" className="h-7 gap-1">
<Copy className="size-3" />
کپی
</Button>
</div>
<div className="mt-4 rounded-lg border border-border bg-muted px-3 py-2">
<div className="text-xs text-muted-foreground">موجودی کل</div>
<div className="mt-1 text-xl font-semibold text-foreground">
{data.wallet.balance}
</div>
<Button variant="outline" size="sm" className="mt-2 h-7 gap-1">
<span className="flex size-4 items-center justify-center rounded-full bg-primary text-[10px] text-primary-foreground">
E
</span>
ارز دیجیتال
<ChevronDown className="size-3" />
</Button>
</div>
<div className="mt-3 flex items-center gap-2">
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowLeftRight className="size-3.5" />
مبادله
</Button>
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowDownRight className="size-3.5" />
خرید
</Button>
<Button variant="outline" size="sm" className="h-8 gap-1">
<ArrowUpRight className="size-3.5" />
ارسال
</Button>
</div>
<div className="mt-4 flex items-center justify-between text-xs font-medium text-muted-foreground">
<span>دارایی‌های برتر</span>
<span className="text-primary">مشاهده همه</span>
</div>
<div className="mt-2 space-y-2">
{walletAssets.map((asset) => (
<div
key={asset.symbol}
className="flex items-center justify-between rounded-md px-2 py-1 text-sm hover:bg-accent/50"
>
<div className="flex items-center gap-2">
<TokenBadge item={asset} />
<span>
{asset.name}
<span className="ms-1 text-xs text-muted-foreground">
{asset.symbol}
</span>
</span>
</div>
<span className="text-xs text-muted-foreground">۰٫۰۰۹۵</span>
</div>
))}
</div>
<DropdownMenuSeparator className="my-3" />
<DropdownMenuItem className="flex items-center justify-between">
<div>
<div className="text-sm">تأیید ۲FA</div>
<div className="text-xs text-muted-foreground">
برای امنیت بیشتر ۲FA را فعال کنید
</div>
</div>
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-xs text-primary">
تأیید
</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>تنظیمات کیف پول</DropdownMenuItem>
<DropdownMenuItem className="text-rose-500">
قطع اتصال کیف پول
</DropdownMenuItem>
<div className="mt-2 text-[11px] text-muted-foreground">
نسخه ۱٫۵٫۶۹ قوانین و مقررات
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function FavoritesMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full">
<Star className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">علاقه‌مندی‌های من (۴)</span>
<button className="text-xs text-muted-foreground">پاک کردن</button>
</div>
<div className="relative mt-3">
<Search className="pointer-events-none absolute top-1/2 start-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="جستجوی توکن..."
className="h-8 ps-8 text-sm"
aria-label="جستجوی توکن"
/>
</div>
<div className="mt-3 max-h-56 space-y-2 overflow-auto pe-1">
{favorites.map((token) => (
<div
key={token.symbol}
className="flex items-center justify-between rounded-md px-2 py-1 text-sm hover:bg-accent/50"
>
<div className="flex items-center gap-2">
<TokenBadge item={token} />
<span>
{token.name}
<span className="ms-1 text-xs text-muted-foreground">
{token.symbol}
</span>
</span>
</div>
<Star className="size-3 text-amber-400" />
</div>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function NotificationsMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full">
<Bell className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-80 p-3 text-start">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">اعلان‌ها</span>
<button className="text-xs text-primary">علامت‌گذاری همه به‌عنوان خوانده‌شده</button>
</div>
<div className="mt-3 space-y-2">
{notifications.map((item) => (
<div
key={item.title}
className="flex items-start gap-2 rounded-md border border-border px-2 py-2"
>
<span
aria-hidden="true"
className={cn(
"mt-1 flex size-7 items-center justify-center rounded-full bg-accent text-xs",
item.tone,
)}
/>
<div className="flex-1">
<div className="text-sm font-medium text-foreground">
{item.title}
</div>
<div className="text-xs text-muted-foreground">
{item.detail}
</div>
</div>
<span className="text-xs text-muted-foreground">{item.time}</span>
</div>
))}
</div>
<button className="mt-3 w-full rounded-md border border-border py-1 text-xs text-muted-foreground">
مشاهده همه اعلان‌ها
</button>
</DropdownMenuContent>
</DropdownMenu>
);
}
function UserMenu() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 rounded-full border border-transparent px-2 py-1 text-sm hover:bg-accent/50">
<Avatar className="size-7">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback className="text-xs">
{data.user.name
.split(" ")
.map((part) => part[0])
.join("")}
</AvatarFallback>
</Avatar>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-64 p-3 text-start">
<div className="flex items-center gap-3">
<Avatar className="size-9">
<AvatarImage src={data.user.avatar} alt={data.user.name} />
<AvatarFallback className="text-xs">
{data.user.name
.split(" ")
.map((part) => part[0])
.join("")}
</AvatarFallback>
</Avatar>
<div>
<div className="text-sm font-medium text-foreground">
{data.user.name}
</div>
<div className="text-xs text-muted-foreground">
{data.user.email}
</div>
</div>
</div>
<DropdownMenuSeparator className="my-2" />
<DropdownMenuItem className="gap-2">
<Moon className="size-4" />
حالت تیره
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Settings className="size-4" />
تنظیمات حساب
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Wallet className="size-4" />
ترجیح‌ها
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<HelpCircle className="size-4" />
راهنمای اعضای تیم
</DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<Gift className="size-4" />
انجمن
</DropdownMenuItem>
<DropdownMenuSeparator className="my-2" />
<DropdownMenuItem className="gap-2 text-rose-500">
<LogOut className="size-4" />
خروج
</DropdownMenuItem>
<div className="mt-2 text-[11px] text-muted-foreground">
نسخه ۲٫۸٫۶۹ قوانین و مقررات
</div>
</DropdownMenuContent>
</DropdownMenu>
);
}
function MobileMenu() {
return (
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
size="icon"
className="md:hidden"
aria-label="باز کردن منو"
>
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" dir="rtl" className="w-72 p-0 text-start">
<SheetHeader className="px-4 pt-4">
<SheetTitle className="flex items-center gap-2">
<span className="flex size-8 items-center justify-center rounded-full bg-primary/10 text-primary">
<Wallet className="size-4" />
</span>
{data.brand.name}
</SheetTitle>
</SheetHeader>
<ScrollArea className="min-h-0 flex-1">
<div className="flex flex-col gap-2 px-4 py-4">
{navLinks.map((link) => (
<a
key={link.label}
href={link.href}
className={cn(
"rounded-md px-3 py-2 text-sm",
link.isActive
? "bg-accent font-medium"
: "text-muted-foreground hover:bg-accent/50",
)}
>
{link.label}
</a>
))}
</div>
<div className="px-4 pb-4">
<WalletMenu />
</div>
</ScrollArea>
</SheetContent>
</Sheet>
);
}
function BottomNav() {
return (
<nav className="fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/95 backdrop-blur md:hidden">
<div className="grid grid-cols-4">
{navLinks.map((link) => {
const Icon = link.icon;
return (
<a
key={link.label}
href={link.href}
className={cn(
"flex flex-col items-center gap-1 py-2 text-xs",
link.isActive
? "text-primary"
: "text-muted-foreground hover:text-foreground",
)}
>
<Icon className="size-5" aria-hidden="true" />
<span className="sr-only">{link.label}</span>
<span aria-hidden="true">{link.label}</span>
</a>
);
})}
</div>
</nav>
);
}
interface ApplicationShell14Props {
className?: string;
}
export function ApplicationShell14({ className }: ApplicationShell14Props) {
return (
<div
dir="rtl"
lang="fa"
className={cn("min-h-svh bg-background", className)}
>
<div className="min-h-svh w-full bg-background">
<div className="border-b border-border">
<div className="mx-auto w-full max-w-7xl px-4">
<div className="grid grid-cols-[1fr_auto_1fr] items-center gap-3 py-2 text-xs text-muted-foreground">
<div />
<div className="flex flex-wrap items-center justify-center gap-2 text-center">
<Gift className="size-3.5 text-primary" />
<span>
۲۵ ریال پاداش خوش‌آمدگویی اولین مبادله را دریافت کنید
</span>
<button className="text-primary">دریافت کنید</button>
</div>
<div className="flex justify-end">
<button className="text-muted-foreground" aria-label="بستن">
&times;
</button>
</div>
</div>
</div>
</div>
<div className="border-b border-border">
<div className="mx-auto w-full max-w-7xl px-4">
<div className="flex items-center justify-between gap-4 py-3">
<div className="flex items-center gap-2 md:gap-3">
<MobileMenu />
<div className="flex size-9 items-center justify-center rounded-full bg-primary/10 text-primary md:hidden">
<Wallet className="size-4" />
</div>
<div className="hidden size-9 items-center justify-center rounded-full bg-primary/10 text-primary md:flex">
<Wallet className="size-5" />
</div>
<nav className="hidden items-center gap-1 md:flex">
{navLinks.map((link) => (
<HeaderTab key={link.label} item={link} />
))}
</nav>
</div>
<div className="hidden w-full justify-self-center md:block md:max-w-md">
<SearchField />
</div>
<div className="flex items-center gap-1 md:gap-2">
<WalletMenu />
<div className="hidden md:block">
<FavoritesMenu />
</div>
<div className="hidden md:block">
<NotificationsMenu />
</div>
<UserMenu />
</div>
</div>
</div>
</div>
<div className="border-b border-border md:hidden">
<div className="mx-auto w-full max-w-7xl px-4 py-3">
<SearchField />
</div>
</div>
<div className="mx-auto w-full max-w-7xl px-4">
<div className="min-h-[360px]" />
</div>
</div>
<BottomNav />
</div>
);
}