سفارش با نسخه
پیشنمایش تمامصفحهسفارشِ داروی نسخهای با عکسِ نسخه یا کدِ رهگیریِ نسخهٔ الکترونیک و کدِ ملی، برای چه کسی، نشانیِ تحویل و راهنمای مراحلِ بعد از ثبت.
۱. نصب کامپوننتها
این بلاک از ۱۰ آیتم دیگ استفاده میکند: buttoncontrol-sizefile-uploadiconspersianradio-cardradio-grouptext-fieldtoasttoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-card.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-card.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-card.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-card.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
pharmacy-prescription.tsx
"use client"
import * as React from "react"
import { ClockIcon, FileTextIcon, LocationIcon, TruckIcon, WalletIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { FileUpload } from "@/components/ui/file-upload"
import { RadioCard } from "@/components/ui/radio-card"
import { RadioGroup } from "@/components/ui/radio-group"
import { TextField } from "@/components/ui/text-field"
import { toast } from "@/components/ui/toast"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { isValidNationalId, toEnDigits, toFaDigits } from "@/lib/persian"
const HOW = [
{ icon: <FileTextIcon />, title: "نسخه را ثبت کن", text: "عکسِ نسخهٔ کاغذی یا کدِ رهگیریِ نسخهٔ الکترونیک." },
{ icon: <ClockIcon />, title: "داروخانه قیمت میدهد", text: "معمولاً تا ۱۵ دقیقه، با سهمِ بیمه و داروهای جایگزین." },
{ icon: <WalletIcon />, title: "پرداخت میکنی", text: "بعد از تأییدِ تو و فقط مبلغِ نهایی." },
{ icon: <TruckIcon />, title: "پیک میآورد", text: "داروی یخچالی با کیفِ سرد." },
]
/**
* سفارشِ داروی نسخهای: یک انتخابِ تکی بینِ «عکسِ نسخه» و «نسخهٔ الکترونیک» (سگمنت، نه دو فرم کنارِ هم)،
* بارگذاریِ تا ۳ عکس یا کدِ ملی و کدِ رهگیری با خطای کنارِ فیلد، برای چه کسی، و نشانیِ تحویل. ستونِ
* کنار مراحل را میگوید تا کاربر بداند بعد از ثبت چه میشود و الان پولی پرداخت نمیکند.
*/
export default function PharmacyPrescriptionBlock() {
const [method, setMethod] = React.useState("photo")
const [patient, setPatient] = React.useState("me")
const [nationalId, setNationalId] = React.useState("")
const [code, setCode] = React.useState("")
const [blurred, setBlurred] = React.useState({ id: false, code: false })
const idDigits = toEnDigits(nationalId).replace(/\D/g, "")
const idError = !blurred.id || isValidNationalId(idDigits)
? undefined
: !idDigits
? "کدِ ملی را بنویس؛ ۱۰ رقم است."
: idDigits.length !== 10
? `کدِ ملی ۱۰ رقم است، ${toFaDigits(idDigits.length)} رقم وارد شده.`
: "این کدِ ملی معتبر نیست؛ یک بار دیگر از روی کارت نگاه کن."
const codeError = blurred.code && toEnDigits(code).replace(/\D/g, "").length < 6 ? "کدِ رهگیری در پیامکِ پزشک آمده؛ دستکم ۶ رقم." : undefined
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto grid w-full max-w-5xl grid-cols-1 items-start gap-6 p-4 sm:p-6 lg:grid-cols-[minmax(0,1fr)_20rem] lg:gap-8">
<form
className="flex min-w-0 flex-col gap-6"
noValidate
onSubmit={(event) => {
event.preventDefault()
if (method === "code") {
setBlurred({ id: true, code: true })
if (!isValidNationalId(toEnDigits(nationalId).replace(/\D/g, "")) || toEnDigits(code).replace(/\D/g, "").length < 6) return
}
toast.success("نسخه ثبت شد؛ قیمت را تا ۱۵ دقیقهٔ دیگر پیامک میکنیم.")
}}
>
<header className="flex flex-col gap-1">
<h1 className="text-2xl leading-snug font-bold">سفارش با نسخه</h1>
<p className="text-sm text-muted-foreground">الان پولی پرداخت نمیکنی؛ اول قیمت را میبینی.</p>
</header>
<section aria-labelledby="method-title" className="flex flex-col gap-4">
<h2 id="method-title" className="sr-only">
روشِ ثبتِ نسخه
</h2>
<ControlSizeProvider size="lg" mobileSize="default" asChild>
<ToggleGroup type="single" variant="segmented" fullWidth value={method} onValueChange={(value) => value && setMethod(value)} aria-label="روشِ ثبتِ نسخه" className="sm:w-max">
<ToggleGroupItem value="photo">عکسِ نسخه</ToggleGroupItem>
<ToggleGroupItem value="code">نسخهٔ الکترونیک</ToggleGroupItem>
</ToggleGroup>
</ControlSizeProvider>
{method === "photo" ? (
<FileUpload name="prescription" multiple maxFiles={3} accept="image/*" title="عکسِ نسخه را بکش یا انتخاب کن" hint="تا ۳ عکس، هر کدام تا ۵ مگابایت؛ نامِ پزشک و مهر خوانا باشد." />
) : (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<TextField
label="کدِ ملیِ بیمار"
inputMode="numeric"
dir="ltr"
maxLength={10}
value={nationalId}
onValueChange={setNationalId}
onBlur={() => setBlurred((value) => ({ ...value, id: true }))}
invalid={Boolean(idError)}
errorMessage={idError}
autoComplete="off"
/>
<TextField
label="کدِ رهگیریِ نسخه"
inputMode="numeric"
dir="ltr"
value={code}
onValueChange={setCode}
onBlur={() => setBlurred((value) => ({ ...value, code: true }))}
invalid={Boolean(codeError)}
errorMessage={codeError}
description="در پیامکی که بعد از ویزیت گرفتی."
autoComplete="one-time-code"
/>
</div>
)}
</section>
<section aria-labelledby="patient-title" className="flex flex-col gap-3">
<h2 id="patient-title" className="text-lg leading-snug font-bold">
برای چه کسی؟
</h2>
<RadioGroup value={patient} onValueChange={setPatient} aria-labelledby="patient-title" className="grid grid-cols-2 gap-3">
<RadioCard layout="tile" value="me" title="خودم" description="آرمیتا رستگار" />
<RadioCard layout="tile" value="other" title="شخصِ دیگر" description="کدِ ملیِ او را بنویس" />
</RadioGroup>
</section>
<section aria-labelledby="address-title" className="flex flex-col gap-3">
<h2 id="address-title" className="text-lg leading-snug font-bold">
تحویل به
</h2>
<div className="flex items-start gap-3 rounded-xl border border-border bg-card p-4">
<LocationIcon aria-hidden className="mt-0.5 size-5 shrink-0 text-muted-foreground" />
<span className="flex min-w-0 flex-1 flex-col gap-0.5">
<span className="font-semibold">خانه</span>
<span className="text-sm text-muted-foreground">تهران، خیابانِ آزادی، کوچهٔ نسترن، پلاکِ ۱۲، واحدِ ۴</span>
</span>
<ControlSizeProvider size="sm" asChild>
<Button type="button" variant="ghost" className="-me-2 text-brand-secondary">
تغییر
</Button>
</ControlSizeProvider>
</div>
</section>
<Button type="submit" fullWidth className="mt-2">
ثبتِ نسخه و دریافتِ قیمت
</Button>
</form>
<aside aria-labelledby="how-title" className="flex flex-col gap-4 rounded-xl border border-border bg-card p-5 lg:sticky lg:top-6">
<h2 id="how-title" className="font-bold">
بعد از ثبت چه میشود؟
</h2>
<ol className="flex flex-col gap-4">
{HOW.map((item) => (
<li key={item.title} className="flex gap-3">
<span aria-hidden className="relative flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-subtle text-primary-subtle-foreground [&>svg]:size-4">
{item.icon}
</span>
<span className="flex flex-col gap-0.5">
<span className="text-sm font-semibold">{item.title}</span>
<span className="text-xs leading-5 text-muted-foreground">{item.text}</span>
</span>
</li>
))}
</ol>
</aside>
</main>
</ControlSizeProvider>
)
}