address card provider
This commit is contained in:
95
components/AddressCard.tsx
Normal file
95
components/AddressCard.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
155
components/context/Addresscontext.tsx
Normal file
155
components/context/Addresscontext.tsx
Normal 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>;
|
||||
};
|
||||
Reference in New Issue
Block a user