198 lines
6.8 KiB
TypeScript
198 lines
6.8 KiB
TypeScript
"use client";
|
||
|
||
import { useMemo, useState, useTransition } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { createAddress, deleteAddress, type AddressInput } from "@/lib/api/addresses";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Input } from "@/components/ui/input";
|
||
import type { Address, AddressLabel, City, Neighborhood } from "@/types/api";
|
||
|
||
const LABEL_OPTIONS: { value: AddressLabel; label: string }[] = [
|
||
{ value: "home", label: "خانه" },
|
||
{ value: "work", label: "محل کار" },
|
||
{ value: "other", label: "سایر" },
|
||
];
|
||
|
||
const emptyForm = {
|
||
label: "home" as AddressLabel,
|
||
title: "",
|
||
full_address: "",
|
||
plaque: "",
|
||
floor: "",
|
||
unit: "",
|
||
recipient_name: "",
|
||
recipient_phone: "",
|
||
city_uuid: "",
|
||
neighborhood_uuid: "",
|
||
is_default: false,
|
||
};
|
||
|
||
export function AddressList({
|
||
addresses,
|
||
cities,
|
||
neighborhoods,
|
||
}: {
|
||
addresses: Address[];
|
||
cities: City[];
|
||
neighborhoods: Neighborhood[];
|
||
}) {
|
||
const router = useRouter();
|
||
const [isAdding, setIsAdding] = useState(false);
|
||
const [form, setForm] = useState(emptyForm);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const [isPending, startTransition] = useTransition();
|
||
|
||
const filteredNeighborhoods = useMemo(
|
||
() => neighborhoods.filter((n) => n.city.uuid === form.city_uuid),
|
||
[neighborhoods, form.city_uuid],
|
||
);
|
||
|
||
function handleDelete(uuid: string) {
|
||
startTransition(async () => {
|
||
await deleteAddress(uuid);
|
||
router.refresh();
|
||
});
|
||
}
|
||
|
||
function handleSubmit(e: React.FormEvent) {
|
||
e.preventDefault();
|
||
setError(null);
|
||
startTransition(async () => {
|
||
const input: AddressInput = {
|
||
...form,
|
||
neighborhood_uuid: form.neighborhood_uuid || undefined,
|
||
latitude: 0,
|
||
longitude: 0,
|
||
};
|
||
const result = await createAddress(input);
|
||
if (result.ok) {
|
||
setForm(emptyForm);
|
||
setIsAdding(false);
|
||
router.refresh();
|
||
} else {
|
||
setError(result.message);
|
||
}
|
||
});
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-col gap-4">
|
||
<div className="flex flex-col gap-3">
|
||
{addresses.map((address) => (
|
||
<div key={address.uuid} className="flex items-start justify-between gap-3 rounded-2xl border border-border p-4">
|
||
<div className="flex flex-col gap-1 text-sm">
|
||
<span className="font-medium">
|
||
{LABEL_OPTIONS.find((o) => o.value === address.label)?.label} {address.is_default && "· پیشفرض"}
|
||
</span>
|
||
<span className="text-muted-foreground">{address.full_address}</span>
|
||
<span className="text-muted-foreground">
|
||
{address.recipient_name} — {address.recipient_phone}
|
||
</span>
|
||
</div>
|
||
<Button variant="ghost" size="sm" disabled={isPending} onClick={() => handleDelete(address.uuid)}>
|
||
حذف
|
||
</Button>
|
||
</div>
|
||
))}
|
||
{addresses.length === 0 && !isAdding && <p className="text-sm text-muted-foreground">آدرسی ثبت نشده است.</p>}
|
||
</div>
|
||
|
||
{isAdding ? (
|
||
<form onSubmit={handleSubmit} className="flex flex-col gap-3 rounded-2xl border border-border p-4">
|
||
<div className="flex gap-2">
|
||
{LABEL_OPTIONS.map((opt) => (
|
||
<button
|
||
key={opt.value}
|
||
type="button"
|
||
onClick={() => setForm((f) => ({ ...f, label: opt.value }))}
|
||
className={`rounded-full px-3 py-1 text-xs ${form.label === opt.value ? "bg-primary text-primary-foreground" : "border border-border"}`}
|
||
>
|
||
{opt.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
<Input
|
||
placeholder="آدرس کامل"
|
||
value={form.full_address}
|
||
onChange={(e) => setForm((f) => ({ ...f, full_address: e.target.value }))}
|
||
required
|
||
/>
|
||
<div className="grid grid-cols-3 gap-2">
|
||
<Input placeholder="پلاک" value={form.plaque} onChange={(e) => setForm((f) => ({ ...f, plaque: e.target.value }))} />
|
||
<Input placeholder="طبقه" value={form.floor} onChange={(e) => setForm((f) => ({ ...f, floor: e.target.value }))} />
|
||
<Input placeholder="واحد" value={form.unit} onChange={(e) => setForm((f) => ({ ...f, unit: e.target.value }))} />
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-2">
|
||
<select
|
||
value={form.city_uuid}
|
||
onChange={(e) => setForm((f) => ({ ...f, city_uuid: e.target.value, neighborhood_uuid: "" }))}
|
||
className="h-11 rounded-xl border border-border bg-background px-3 text-sm"
|
||
required
|
||
>
|
||
<option value="">شهر</option>
|
||
{cities.map((city) => (
|
||
<option key={city.uuid} value={city.uuid}>
|
||
{city.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
<select
|
||
value={form.neighborhood_uuid}
|
||
onChange={(e) => setForm((f) => ({ ...f, neighborhood_uuid: e.target.value }))}
|
||
className="h-11 rounded-xl border border-border bg-background px-3 text-sm"
|
||
disabled={!form.city_uuid}
|
||
>
|
||
<option value="">محله (اختیاری)</option>
|
||
{filteredNeighborhoods.map((n) => (
|
||
<option key={n.uuid} value={n.uuid}>
|
||
{n.name}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<Input
|
||
placeholder="نام گیرنده"
|
||
value={form.recipient_name}
|
||
onChange={(e) => setForm((f) => ({ ...f, recipient_name: e.target.value }))}
|
||
required
|
||
/>
|
||
<Input
|
||
type="tel"
|
||
dir="ltr"
|
||
placeholder="09123456789"
|
||
value={form.recipient_phone}
|
||
onChange={(e) => setForm((f) => ({ ...f, recipient_phone: e.target.value }))}
|
||
required
|
||
/>
|
||
|
||
<label className="flex items-center gap-2 text-sm">
|
||
<input
|
||
type="checkbox"
|
||
checked={form.is_default}
|
||
onChange={(e) => setForm((f) => ({ ...f, is_default: e.target.checked }))}
|
||
/>
|
||
آدرس پیشفرض
|
||
</label>
|
||
|
||
{error && <p className="text-sm text-destructive">{error}</p>}
|
||
|
||
<div className="flex gap-2">
|
||
<Button type="submit" disabled={isPending}>
|
||
{isPending ? "در حال ثبت..." : "ثبت آدرس"}
|
||
</Button>
|
||
<Button type="button" variant="outline" onClick={() => setIsAdding(false)}>
|
||
انصراف
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
) : (
|
||
<Button variant="outline" onClick={() => setIsAdding(true)}>
|
||
+ افزودن آدرس جدید
|
||
</Button>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|