winofy-front/src/components/customer/checkout-form.tsx
2026-08-19 13:01:19 +03:30

158 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { useState, useTransition } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { submitCheckout } from "@/lib/api/checkout";
import { Button } from "@/components/ui/button";
import type { Address, Cart, DeliveryType, PaymentMethod } from "@/types/api";
const PAYMENT_LABELS_FA: Record<PaymentMethod, string> = {
wallet: "کیف پول وینوفای",
online: "درگاه بانکی",
cash_on_delivery: "پرداخت در محل",
};
export function CheckoutForm({ cart, addresses }: { cart: Cart; addresses: Address[] }) {
const router = useRouter();
const [addressUuid, setAddressUuid] = useState(addresses.find((a) => a.is_default)?.uuid ?? addresses[0]?.uuid ?? "");
const [deliveryType, setDeliveryType] = useState<DeliveryType>("express");
const [scheduledAt, setScheduledAt] = useState("");
const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>("cash_on_delivery");
const [notes, setNotes] = useState("");
const [error, setError] = useState<string | null>(null);
const [isPending, startTransition] = useTransition();
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
if (!addressUuid) {
setError("لطفاً یک آدرس انتخاب کنید.");
return;
}
if (deliveryType === "scheduled" && !scheduledAt) {
setError("لطفاً زمان ارسال را مشخص کنید.");
return;
}
startTransition(async () => {
const result = await submitCheckout({
address_uuid: addressUuid,
delivery_type: deliveryType,
scheduled_at: deliveryType === "scheduled" ? new Date(scheduledAt).toISOString() : null,
payment_method: paymentMethod,
notes,
});
if (result.ok) {
router.push(`/order-groups/${result.data.uuid}`);
} else {
setError(result.message);
}
});
}
if (addresses.length === 0) {
return (
<div className="flex flex-col items-center gap-3 rounded-2xl border border-border p-6 text-center">
<p className="text-sm text-muted-foreground">برای ثبت سفارش ابتدا یک آدرس اضافه کنید.</p>
<Link href="/addresses">
<Button>افزودن آدرس</Button>
</Link>
</div>
);
}
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
<section className="flex flex-col gap-2">
<h2 className="text-sm font-semibold text-muted-foreground">آدرس تحویل</h2>
{addresses.map((address) => (
<label
key={address.uuid}
className={`flex cursor-pointer items-start gap-3 rounded-2xl border p-3 text-sm ${addressUuid === address.uuid ? "border-primary" : "border-border"}`}
>
<input
type="radio"
name="address"
checked={addressUuid === address.uuid}
onChange={() => setAddressUuid(address.uuid)}
className="mt-1"
/>
<span>
<span className="block font-medium">{address.title || address.full_address}</span>
<span className="block text-muted-foreground">{address.full_address}</span>
</span>
</label>
))}
<Link href="/addresses" className="text-xs text-primary underline underline-offset-4">
مدیریت آدرس‌ها
</Link>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-sm font-semibold text-muted-foreground">زمان ارسال</h2>
<div className="flex gap-2">
<button
type="button"
onClick={() => setDeliveryType("express")}
className={`flex-1 rounded-xl border px-3 py-2 text-sm ${deliveryType === "express" ? "border-primary bg-primary/10" : "border-border"}`}
>
ارسال فوری
</button>
<button
type="button"
onClick={() => setDeliveryType("scheduled")}
className={`flex-1 rounded-xl border px-3 py-2 text-sm ${deliveryType === "scheduled" ? "border-primary bg-primary/10" : "border-border"}`}
>
زمان‌بندی شده
</button>
</div>
{deliveryType === "scheduled" && (
<input
type="datetime-local"
value={scheduledAt}
onChange={(e) => setScheduledAt(e.target.value)}
className="h-11 rounded-xl border border-border bg-background px-3 text-sm"
/>
)}
</section>
<section className="flex flex-col gap-2">
<h2 className="text-sm font-semibold text-muted-foreground">روش پرداخت</h2>
<div className="flex flex-col gap-2">
{(Object.keys(PAYMENT_LABELS_FA) as PaymentMethod[]).map((method) => (
<label
key={method}
className={`flex cursor-pointer items-center gap-3 rounded-2xl border p-3 text-sm ${paymentMethod === method ? "border-primary" : "border-border"}`}
>
<input type="radio" name="payment" checked={paymentMethod === method} onChange={() => setPaymentMethod(method)} />
{PAYMENT_LABELS_FA[method]}
</label>
))}
</div>
</section>
<section className="flex flex-col gap-2">
<h2 className="text-sm font-semibold text-muted-foreground">توضیحات (اختیاری)</h2>
<textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={2}
className="rounded-xl border border-border bg-background px-3 py-2 text-sm"
/>
</section>
{error && <p className="rounded-lg bg-destructive/10 px-3 py-2 text-sm text-destructive">{error}</p>}
<div className="flex items-center justify-between rounded-2xl border border-border p-4 font-semibold">
<span>مبلغ قابل پرداخت</span>
<span>{cart.grand_total.toLocaleString("fa-IR")} تومان</span>
</div>
<Button type="submit" size="lg" disabled={isPending}>
{isPending ? "در حال ثبت سفارش..." : "ثبت نهایی سفارش"}
</Button>
</form>
);
}