بلاک‌ها · موبایل

خانهٔ کیف پول (اپ)

پیش‌نمایش تمام‌صفحه

خانهٔ اپِ بانکی: نوارِ بالا با عکس و اعلان، کارتِ تیرهٔ موجودی با نمایش/پنهان، چهار اقدامِ سریع، تراکنش‌های اخیر با ردیف‌های لمسی و نوارِ تبِ پایین.

۱. نصب کامپوننت‌ها

این بلاک از ۸ آیتم دیگ استفاده می‌کند: app-baravatarbadgebottom-navigationbuttoncontrol-sizeiconspersian

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/app-bar.json https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/bottom-navigation.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

mobile-wallet.tsx
"use client"

import * as React from "react"

import { ArrowUpIcon, BarChartIcon, BellIcon, CreditCardIcon, DashboardIcon, EyeIcon, EyeOffIcon, PhoneIcon, PlusIcon, ReceiptIcon, UserIcon, WalletIcon } from "@/components/icons"
import { AppBar, AppBarActions, AppBarLeading, AppBarTitle } from "@/components/ui/app-bar"
import { Avatar } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { BottomNavigation, BottomNavigationItem } from "@/components/ui/bottom-navigation"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { formatCurrency, toFaDigits } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"

const ACTIONS = [
  { label: "انتقال", icon: <ArrowUpIcon className="rotate-45" /> },
  { label: "افزایش", icon: <PlusIcon /> },
  { label: "قبض", icon: <ReceiptIcon /> },
  { label: "شارژ", icon: <PhoneIcon /> },
]
const TRANSACTIONS = [
  { title: "کافه رست", note: "امروز، ۱۰:۲۴", amount: -185_000 },
  { title: "واریزِ حقوق", note: "دیروز", amount: 32_000_000 },
  { title: "شارژِ همراه اول", note: "۱۲ مهر", amount: -50_000 },
  { title: "انتقال به سارا محمدی", note: "۱۱ مهر", amount: -1_200_000 },
]

/**
 * خانهٔ کیف پول (پوستهٔ اپ): نوارِ بالا با عکسِ کاربر و اعلان، کارتِ موجودی تیره با
 * نمایش/پنهان، چهار اقدامِ سریع، تراکنش‌های اخیر با ردیف‌های کاملاً لمسی، و نوارِ تبِ پایین.
 */
export default function MobileWalletBlock() {
  const [hidden, setHidden] = React.useState(false)
  const [tab, setTab] = React.useState("home")

  return (
    <ControlSizeProvider size="xl" asChild>
      <div className="mx-auto flex min-h-svh w-full max-w-md flex-col bg-background">
        <AppBar sticky>
          <AppBarLeading>
            <Avatar alt="مهسا کریمی" src={stockImages.personWoman1.src} />
          </AppBarLeading>
          <AppBarTitle className="flex flex-col items-start text-start">
            <span className="text-xs font-normal text-muted-foreground">سلام،</span>
            مهسا
          </AppBarTitle>
          <AppBarActions>
            <Button variant="ghost" size="icon" aria-label="اعلان‌ها، ۳ تازه" className="relative">
              <BellIcon />
              <span aria-hidden className="absolute end-2 top-2 size-2 rounded-full bg-destructive" />
            </Button>
          </AppBarActions>
        </AppBar>

        <main className="flex flex-1 flex-col gap-6 p-4">
          <section aria-label="موجودی" className="flex flex-col gap-4 rounded-xl bg-inverse p-5 text-[var(--dig-white)]">
            <div className="flex items-center justify-between">
              <span className="text-sm text-[var(--dig-neutral-400)]">موجودی کیف پول</span>
              <Button variant="ghost" size="icon" aria-label={hidden ? "نمایشِ موجودی" : "پنهان‌کردنِ موجودی"} aria-pressed={hidden} onClick={() => setHidden((value) => !value)} className="text-[var(--dig-white)] hover:bg-inverse-raised">
                {hidden ? <EyeIcon /> : <EyeOffIcon />}
              </Button>
            </div>
            <span className="text-3xl font-black tabular-nums">{hidden ? "••••••" : formatCurrency(48_250_000)}</span>
            <span className="flex items-center gap-2 text-sm text-[var(--dig-neutral-400)]">
              <CreditCardIcon aria-hidden className="size-4" />
              <bdi dir="ltr">{toFaDigits("6037 •••• •••• 5678")}</bdi>
            </span>
          </section>

          <nav aria-label="اقدام‌های سریع" className="grid grid-cols-4 gap-3">
            {ACTIONS.map((action) => (
              <Button key={action.label} variant="ghost" className="h-auto flex-col gap-2 rounded-xl bg-subtle py-4 font-normal hover:bg-muted">
                <span aria-hidden className="flex size-10 items-center justify-center rounded-full bg-card text-brand-secondary [&>svg]:size-5">
                  {action.icon}
                </span>
                <span className="text-sm">{action.label}</span>
              </Button>
            ))}
          </nav>

          <section aria-labelledby="wallet-recent" className="flex flex-col gap-2">
            <div className="flex items-center justify-between">
              <h2 id="wallet-recent" className="font-bold">
                تراکنش‌های اخیر
              </h2>
              <Button variant="ghost">همه</Button>
            </div>
            <ul className="flex flex-col divide-y divide-border">
              {TRANSACTIONS.map((item) => (
                <li key={item.title + item.note}>
                  <a href="#" className="flex min-h-16 items-center gap-3 py-3">
                    <span aria-hidden className="flex size-11 shrink-0 items-center justify-center rounded-full bg-subtle [&>svg]:size-5">
                      {item.amount > 0 ? <WalletIcon className="text-success" /> : <ReceiptIcon className="text-muted-foreground" />}
                    </span>
                    <span className="flex flex-1 flex-col">
                      <span className="font-medium">{item.title}</span>
                      <span className="text-sm text-muted-foreground">{item.note}</span>
                    </span>
                    <span className={`font-bold tabular-nums ${item.amount > 0 ? "text-success" : ""}`}>
                      <bdi dir="ltr">{item.amount > 0 ? "+" : "−"}{toFaDigits(Math.abs(item.amount).toLocaleString("en-US"))}</bdi>
                    </span>
                  </a>
                </li>
              ))}
            </ul>
          </section>
        </main>

        <BottomNavigation value={tab} onValueChange={setTab} indicator="pill" fixed={false} className="sticky bottom-0 pb-[env(safe-area-inset-bottom)]">
          <BottomNavigationItem value="home" icon={<DashboardIcon />} label="خانه" />
          <BottomNavigationItem value="cards" icon={<CreditCardIcon />} label="کارت‌ها" />
          <BottomNavigationItem value="reports" icon={<BarChartIcon />} label="گزارش" />
          <BottomNavigationItem value="profile" icon={<UserIcon />} label="حساب" badge={<Badge color="destructive">{toFaDigits(1)}</Badge>} />
        </BottomNavigation>
      </div>
    </ControlSizeProvider>
  )
}