address card provider

This commit is contained in:
haniyeroozmand
2026-04-29 12:44:33 +03:30
parent 333c2bf025
commit 0a6bbf425a
4 changed files with 274 additions and 305 deletions

View File

@@ -0,0 +1,95 @@
import React from 'react';
import { type Address } from "@/public/src/services/address/api";
// آدرس ایمپورت کانتکست را بر اساس مسیر فایل خود تنظیم کنید:
import { useAddressContext } from './context/Addresscontext';
interface AddressCardProps {
address: Address;
// فقط پراپ address باقی می‌ماند
}
export default function AddressCard({ address }: AddressCardProps) {
// دریافت وضعیت و توابع از کانتکست
const {
selectedAddressId,
setSelectedAddressId,
handleEditClick,
handleDeleteAddress
} = useAddressContext();
// محاسبه وضعیت انتخاب شدن بر اساس آیدی کارت و آیدی ذخیره شده در کانتکست
const isSelected = selectedAddressId === address.id;
return (
<label
className={`relative flex flex-col gap-4 p-3 cursor-pointer rounded-2xl border transition-all duration-300 ${
isSelected
? 'border-[#ffb900] bg-[#ffb900]/[0.03] ring-1 ring-[#ffb900]/20 shadow-sm'
: 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'
}`}
>
<div className="flex items-start gap-4">
{/* رادیو باتن */}
<div className="pt-0.5 relative flex items-center justify-center">
<input
type="radio"
name="selectedAddress"
value={address.id}
checked={isSelected}
onChange={() => setSelectedAddressId(address.id)} // استفاده از تابع کانتکست
className="peer appearance-none w-[18px] h-[18px] border-[1.5px] border-gray-300 rounded-full checked:border-[#ffb900] checked:bg-[#ffb900] cursor-pointer transition-all focus:outline-none focus:ring-2 focus:ring-[#ffb900]/20 focus:ring-offset-1 bg-white"
/>
{/* دایره سفید مرکزی */}
<div className="pointer-events-none absolute w-[10px] h-[10px] rounded-full bg-white opacity-0 peer-checked:opacity-100 transition-opacity"></div>
</div>
<div className="flex-1 space-y-3">
{/* عنوان و متن آدرس */}
<div>
{address.title && (
<span className="inline-block mb-2 bg-gray-50 text-gray-500 border border-gray-200 text-[11px] font-medium px-2.5 py-0.5 rounded-full">
{address.title}
</span>
)}
<p className="font-medium text-gray-800 leading-relaxed text-sm md:text-base">
{address.addressLine}
</p>
</div>
{/* جزئیات آدرس */}
<div className="flex flex-wrap items-center text-[13px] text-gray-500 gap-x-2 gap-y-1.5">
<span>{address.recipientName}</span>
<span className="text-gray-300 text-[10px]"></span>
<span dir="ltr">{address.phone}</span>
<span className="text-gray-300 text-[10px]"></span>
<span>کد پستی: {address.postalCode}</span>
<span className="text-gray-300 text-[10px]"></span>
<span>
پلاک {address.plaque}
{address.unit ? `، واحد ${address.unit}` : ''}
</span>
</div>
</div>
</div>
{/* نوار دکمه‌های عملیاتی */}
<div className="flex items-center justify-end gap-2 pt-3 mt-1 border-t border-gray-100/80">
<button
type="button"
onClick={(e) => handleEditClick(address, e)} // استفاده از تابع کانتکست
className="px-3 cursor-pointer py-1.5 text-xs font-medium text-gray-500 transition-colors rounded-lg hover:bg-blue-50 hover:text-blue-600 focus:outline-none"
>
ویرایش آدرس
</button>
<button
type="button"
onClick={(e) => handleDeleteAddress(address.id, e)} // استفاده از تابع کانتکست
className="px-3 cursor-pointer py-1.5 text-xs font-medium text-gray-500 transition-colors rounded-lg hover:bg-red-50 hover:text-red-600 focus:outline-none"
>
حذف
</button>
</div>
</label>
);
}

View File

@@ -0,0 +1,155 @@
'use client';
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { fetchUserAddresses, addAddressApi, updateAddressApi, deleteAddressApi, type Address, type NewAddressData } from "@/public/src/services/address/api";
interface AddressContextType {
addresses: Address[];
selectedAddressId: string | null;
setSelectedAddressId: (id: string | null) => void;
isAddressLoading: boolean;
showNewAddressForm: boolean;
setShowNewAddressForm: (show: boolean) => void;
editingAddressId: string | null;
newAddress: NewAddressData;
handleAddressInputChange: (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => void;
handleAddNewAddress: (e: React.FormEvent) => Promise<void>;
handleEditClick: (address: Address, e: any) => void;
handleUpdateAddress: () => Promise<void>;
handleCancelForm: () => void;
handleDeleteAddress: (addressId: string, e: React.MouseEvent) => Promise<void>;
fetchAddresses: () => Promise<void>;
}
export const AddressContext = createContext<AddressContextType | undefined>(undefined);
export const useAddressContext = () => {
const context = useContext(AddressContext);
if (!context) {
throw new Error("useAddressContext must be used within an AddressProvider");
}
return context;
};
export const AddressProvider = ({ children }: { children: ReactNode }) => {
const [addresses, setAddresses] = useState<Address[]>([]);
const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null);
const [isAddressLoading, setIsAddressLoading] = useState(true);
const [showNewAddressForm, setShowNewAddressForm] = useState(false);
const [editingAddressId, setEditingAddressId] = useState<string | null>(null);
const initialAddressState: NewAddressData = {
title: 'خانه', recipientName: '', phone: '', province: '', city: '',
postalCode: '', addressLine: '', plaque: '', unit: '', isDefault: false
};
const [newAddress, setNewAddress] = useState<NewAddressData>(initialAddressState);
const fetchAddresses = async () => {
setIsAddressLoading(true);
try {
const token = localStorage.getItem('accessToken') || localStorage.getItem('refreshToken');
if (!token) {
setIsAddressLoading(false);
return;
}
const addressData = await fetchUserAddresses();
if (addressData && addressData.length > 0) {
setAddresses(addressData);
const defaultAddress = addressData.find(addr => addr.isDefault) || addressData[0];
setSelectedAddressId(defaultAddress.id);
} else {
setShowNewAddressForm(true);
}
} catch (error) {
console.error("خطا در دریافت آدرس‌ها:", error);
} finally {
setIsAddressLoading(false);
}
};
useEffect(() => {
fetchAddresses();
}, []);
const handleAddressInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setNewAddress(prev => ({ ...prev, [name]: value }));
};
const handleAddNewAddress = async (e: React.FormEvent) => {
e.preventDefault();
try {
const addedAddress = await addAddressApi(newAddress);
setAddresses(prev => [...prev, addedAddress]);
setSelectedAddressId(addedAddress.id);
setShowNewAddressForm(false);
setNewAddress(initialAddressState);
} catch (error) {
console.error("خطا در ذخیره آدرس:", error);
}
};
const handleEditClick = (address: any, e: any) => {
e.preventDefault();
e.stopPropagation();
setEditingAddressId(address.id);
setNewAddress({
title: address.title || "", recipientName: address.recipientName || "", phone: address.phone || "",
province: address.province || "", city: address.city || "", postalCode: address.postalCode || "",
addressLine: address.addressLine || "", plaque: address.plaque || "", unit: address.unit || "",
isDefault: address.isDefault || false
});
setShowNewAddressForm(true);
};
const handleUpdateAddress = async () => {
if (!editingAddressId) return;
try {
const response = await updateAddressApi(editingAddressId, newAddress);
if (response.success) {
setAddresses(prev => prev.map(addr => addr.id === editingAddressId ? response.data : addr));
setShowNewAddressForm(false);
setEditingAddressId(null);
setNewAddress(initialAddressState);
}
} catch (error) {
console.error("خطا در به‌روزرسانی آدرس:", error);
}
};
const handleCancelForm = () => {
setShowNewAddressForm(false);
setEditingAddressId(null);
setNewAddress(initialAddressState);
};
const handleDeleteAddress = async (addressId: string, e: React.MouseEvent) => {
e.stopPropagation();
const confirmDelete = window.confirm("آیا از حذف این آدرس اطمینان دارید؟");
if (!confirmDelete) return;
try {
const response = await deleteAddressApi(addressId);
if (response.success) {
setAddresses(prev => prev.filter(addr => addr.id !== addressId));
if (selectedAddressId === addressId) setSelectedAddressId(null);
if (editingAddressId === addressId) {
setShowNewAddressForm(false);
setEditingAddressId(null);
setNewAddress(initialAddressState);
}
}
} catch (error) {
console.error("خطا در حذف آدرس:", error);
}
};
const value = {
addresses, selectedAddressId, setSelectedAddressId, isAddressLoading,
showNewAddressForm, setShowNewAddressForm, editingAddressId, newAddress,
handleAddressInputChange, handleAddNewAddress, handleEditClick,
handleUpdateAddress, handleCancelForm, handleDeleteAddress, fetchAddresses
};
return <AddressContext.Provider value={value}>{children}</AddressContext.Provider>;
};