106 lines
4 KiB
TypeScript
106 lines
4 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useTransition } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
import { updateAddress } from "@/lib/api/addresses";
|
||
import type { Address, AddressLabel } from "@/types/api";
|
||
|
||
const ADDRESS_LABEL_FA: Record<AddressLabel, string> = {
|
||
home: "خانه",
|
||
work: "محل کار",
|
||
other: "سایر",
|
||
};
|
||
|
||
/**
|
||
* Doubles as both a plain address-management screen (reached from the profile menu)
|
||
* and a picker (reached from checkout's "تغییر آدرس" with a `next` return path) —
|
||
* the radio always reflects/sets which address is the default, "تأیید" just persists
|
||
* that choice and, if there's somewhere to return to, goes back there.
|
||
*/
|
||
export function AddressSelector({ addresses, next }: { addresses: Address[]; next: string | null }) {
|
||
const router = useRouter();
|
||
const [selectedUuid, setSelectedUuid] = useState(
|
||
() => addresses.find((a) => a.is_default)?.uuid ?? addresses[0]?.uuid ?? null,
|
||
);
|
||
const [error, setError] = useState<string | null>(null);
|
||
const [isPending, startTransition] = useTransition();
|
||
|
||
function handleConfirm() {
|
||
setError(null);
|
||
const currentDefaultUuid = addresses.find((a) => a.is_default)?.uuid;
|
||
if (!selectedUuid || selectedUuid === currentDefaultUuid) {
|
||
if (next) router.push(next);
|
||
return;
|
||
}
|
||
startTransition(async () => {
|
||
const result = await updateAddress(selectedUuid, { is_default: true });
|
||
if (result.ok) {
|
||
if (next) router.push(next);
|
||
else router.refresh();
|
||
} else {
|
||
setError(result.message);
|
||
}
|
||
});
|
||
}
|
||
|
||
return (
|
||
<div className="flex flex-1 flex-col gap-6 px-4 pb-6">
|
||
<div className="flex flex-col gap-3">
|
||
{addresses.map((address) => {
|
||
const isSelected = address.uuid === selectedUuid;
|
||
return (
|
||
<button
|
||
key={address.uuid}
|
||
type="button"
|
||
onClick={() => setSelectedUuid(address.uuid)}
|
||
className={`flex flex-col gap-1 rounded-2xl border p-4 text-right ${
|
||
isSelected ? "border-primary-action" : "border-gray-300"
|
||
}`}
|
||
>
|
||
{/* DOM order intentionally mirrors the screenshot's left→right layout —
|
||
see the inherited dir="rtl" note on the /cart page. */}
|
||
<div className="flex items-center justify-between">
|
||
<span className="text-[14px] font-bold leading-none text-foreground">
|
||
{address.title || ADDRESS_LABEL_FA[address.label]}
|
||
</span>
|
||
<span
|
||
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 ${
|
||
isSelected ? "border-primary-action" : "border-gray-300"
|
||
}`}
|
||
>
|
||
{isSelected && <span className="h-2.5 w-2.5 rounded-full bg-primary-action" />}
|
||
</span>
|
||
</div>
|
||
<p className="text-[13px] leading-normal text-gray-600">{address.full_address}</p>
|
||
</button>
|
||
);
|
||
})}
|
||
|
||
{addresses.length === 0 && (
|
||
<p className="py-4 text-center text-[13px] text-gray-600">هنوز آدرسی ثبت نکردهاید.</p>
|
||
)}
|
||
|
||
<button
|
||
type="button"
|
||
onClick={() => router.push(next ? `/addresses/map?next=${encodeURIComponent(next)}` : "/addresses/map")}
|
||
className="rounded-2xl border border-primary-action py-4 text-[14px] font-bold text-primary-action"
|
||
>
|
||
افزودن آدرس جدید
|
||
</button>
|
||
</div>
|
||
|
||
{error && <p className="text-[13px] text-error">{error}</p>}
|
||
|
||
{addresses.length > 0 && (
|
||
<button
|
||
type="button"
|
||
onClick={handleConfirm}
|
||
disabled={isPending}
|
||
className="mt-auto w-full rounded-2xl bg-primary-action py-4 text-[15px] font-bold text-white disabled:opacity-40"
|
||
>
|
||
{isPending ? "در حال ذخیره..." : "تأیید"}
|
||
</button>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|