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

611 lines
20 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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>
);
}