edit address
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ import {
|
||||
updateAddress,
|
||||
deleteAddress,
|
||||
setDefaultAddress,
|
||||
getAddressById,
|
||||
} from "../service/AddressService";
|
||||
|
||||
export const useGetAddresses = () => {
|
||||
@@ -53,3 +54,11 @@ export const useSetDefaultAddress = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetAddressById = (id: string | null) => {
|
||||
return useQuery({
|
||||
queryKey: ["address", id],
|
||||
queryFn: () => getAddressById(id!),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import Button from '@/components/button/PrimaryButton';
|
||||
import { AddressDisplay } from './AddressDisplay';
|
||||
import { AddressForm } from './AddressForm';
|
||||
import { NominatimReverseGeocodingResponse } from '../../types/Types';
|
||||
import { Location } from 'iconsax-react';
|
||||
|
||||
interface AddressDetailsModalProps {
|
||||
visible: boolean;
|
||||
@@ -15,7 +16,9 @@ interface AddressDetailsModalProps {
|
||||
isDefault: boolean;
|
||||
};
|
||||
isPending: boolean;
|
||||
editMode?: boolean;
|
||||
onClose: () => void;
|
||||
onChangePosition?: () => void;
|
||||
onFormDataChange: (data: Partial<AddressDetailsModalProps['formData']>) => void;
|
||||
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
||||
}
|
||||
@@ -25,7 +28,9 @@ export const AddressDetailsModal = ({
|
||||
selectedAddress,
|
||||
formData,
|
||||
isPending,
|
||||
editMode = false,
|
||||
onClose,
|
||||
onChangePosition,
|
||||
onFormDataChange,
|
||||
onSubmit,
|
||||
}: AddressDetailsModalProps) => {
|
||||
@@ -43,6 +48,22 @@ export const AddressDetailsModal = ({
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className='px-4'>
|
||||
<AddressDisplay selectedAddress={selectedAddress} />
|
||||
{editMode && onChangePosition && (
|
||||
<div className='mt-4 mb-6 flex justify-end'>
|
||||
<Button
|
||||
type='button'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onChangePosition();
|
||||
}}
|
||||
className='!bg-background !w-fit !text-foreground flex items-center gap-2 text-xs'
|
||||
disabled={isPending}
|
||||
>
|
||||
<Location color='#6b7280' variant='Bold' className='size-4' />
|
||||
تغییر موقعیت روی نقشه
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<AddressForm
|
||||
formData={formData}
|
||||
selectedAddress={selectedAddress}
|
||||
@@ -53,7 +74,7 @@ export const AddressDetailsModal = ({
|
||||
type='submit'
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? 'در حال ثبت...' : 'تایید'}
|
||||
{isPending ? (editMode ? 'در حال بهروزرسانی...' : 'در حال ثبت...') : (editMode ? 'بهروزرسانی' : 'تایید')}
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useCreateAddress } from '../../hooks/useAddressData';
|
||||
import { useCreateAddress, useUpdateAddress } from '../../hooks/useAddressData';
|
||||
import { toast } from '@/components/Toast';
|
||||
import { CreateAddressType, NominatimReverseGeocodingResponse } from '../../types/Types';
|
||||
import { CreateAddressType, UpdateAddressType, NominatimReverseGeocodingResponse, Address } from '../../types/Types';
|
||||
|
||||
interface UseAddressFormProps {
|
||||
selectedPosition: [number, number] | null;
|
||||
selectedAddress: NominatimReverseGeocodingResponse | null;
|
||||
initialPhone?: string;
|
||||
editMode?: boolean;
|
||||
addressData?: Address | null;
|
||||
}
|
||||
|
||||
export const useAddressForm = ({
|
||||
selectedPosition,
|
||||
selectedAddress,
|
||||
initialPhone = '',
|
||||
editMode = false,
|
||||
addressData = null,
|
||||
}: UseAddressFormProps) => {
|
||||
const router = useRouter();
|
||||
const { mutate: createAddress, isPending } = useCreateAddress();
|
||||
const { mutate: createAddress, isPending: isCreating } = useCreateAddress();
|
||||
const { mutate: updateAddress, isPending: isUpdating } = useUpdateAddress();
|
||||
const isPending = isCreating || isUpdating;
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
title: '',
|
||||
@@ -26,6 +32,18 @@ export const useAddressForm = ({
|
||||
isDefault: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editMode && addressData) {
|
||||
setFormData({
|
||||
title: addressData.title || '',
|
||||
addressDetails: addressData.address || '',
|
||||
phone: addressData.phone || initialPhone,
|
||||
postalCode: addressData.postalCode || '',
|
||||
isDefault: addressData.isDefault || false,
|
||||
});
|
||||
}
|
||||
}, [editMode, addressData, initialPhone]);
|
||||
|
||||
const handleFormDataChange = (data: Partial<typeof formData>) => {
|
||||
setFormData((prev) => ({ ...prev, ...data }));
|
||||
};
|
||||
@@ -53,7 +71,7 @@ export const useAddressForm = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const addressData: CreateAddressType = {
|
||||
const baseAddressData = {
|
||||
title: formData.title,
|
||||
address: formData.addressDetails,
|
||||
city: selectedAddress.address.city || selectedAddress.address.district || '',
|
||||
@@ -65,16 +83,36 @@ export const useAddressForm = ({
|
||||
isDefault: formData.isDefault,
|
||||
};
|
||||
|
||||
createAddress(addressData, {
|
||||
onSuccess: () => {
|
||||
toast('آدرس با موفقیت ثبت شد', 'success');
|
||||
router.back();
|
||||
},
|
||||
onError: (error) => {
|
||||
const errorMessage = error instanceof Error ? error.message : 'خطا در ثبت آدرس';
|
||||
toast(errorMessage, 'error');
|
||||
},
|
||||
});
|
||||
if (editMode && addressData) {
|
||||
const updateData: UpdateAddressType = {
|
||||
...baseAddressData,
|
||||
id: addressData.id,
|
||||
};
|
||||
|
||||
updateAddress(updateData, {
|
||||
onSuccess: () => {
|
||||
toast('آدرس با موفقیت بهروزرسانی شد', 'success');
|
||||
router.back();
|
||||
},
|
||||
onError: (error) => {
|
||||
const errorMessage = error instanceof Error ? error.message : 'خطا در بهروزرسانی آدرس';
|
||||
toast(errorMessage, 'error');
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const createData: CreateAddressType = baseAddressData;
|
||||
|
||||
createAddress(createData, {
|
||||
onSuccess: () => {
|
||||
toast('آدرس با موفقیت ثبت شد', 'success');
|
||||
router.back();
|
||||
},
|
||||
onError: (error) => {
|
||||
const errorMessage = error instanceof Error ? error.message : 'خطا در ثبت آدرس';
|
||||
toast(errorMessage, 'error');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ConfirmPositionModal } from './components/ConfirmPositionModal';
|
||||
import { AddressDetailsModal } from './components/AddressDetailsModal';
|
||||
import { useAddressForm } from './hooks/useAddressForm';
|
||||
import { fetchAddressFromCoordinates } from './utils/fetchAddress';
|
||||
import { useGetAddressById } from '../hooks/useAddressData';
|
||||
|
||||
const CustomMap = dynamic(
|
||||
() => import('@/components/map/CustomMap'),
|
||||
@@ -17,15 +18,30 @@ const CustomMap = dynamic(
|
||||
);
|
||||
|
||||
function OrderTrackingPage() {
|
||||
|
||||
const params = useSearchParams();
|
||||
const id = params.get('id');
|
||||
const editMode = !!id;
|
||||
|
||||
const { data: addressResponse, isLoading: isLoadingAddress } = useGetAddressById(id);
|
||||
const address = addressResponse?.data;
|
||||
const user = useAuthStore((state) => state.user);
|
||||
const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null);
|
||||
const [selectedAddress, setSelectedAddress] = useState<NominatimReverseGeocodingResponse | null>(null);
|
||||
const [markers, setMarkers] = useState<MarkerData[]>([]);
|
||||
const [mapCenter] = useState<[number, number]>([
|
||||
Number(params?.get('lat')) || 35.6892,
|
||||
Number(params?.get('lon')) || 51.3890
|
||||
]);
|
||||
const [initialPositionSet, setInitialPositionSet] = useState(false);
|
||||
|
||||
const getInitialMapCenter = (): [number, number] => {
|
||||
if (editMode && address) {
|
||||
return [address.latitude, address.longitude];
|
||||
}
|
||||
return [
|
||||
Number(params?.get('lat')) || 35.6892,
|
||||
Number(params?.get('lon')) || 51.3890
|
||||
];
|
||||
};
|
||||
|
||||
const [mapCenter, setMapCenter] = useState<[number, number]>(getInitialMapCenter());
|
||||
const { state: confirmModal, toggle: toggleConfirmModal, set: setConfirmModal } = useToggle();
|
||||
const { state: detailsModal, toggle: _toggleDetailsModal, set: setDetailsModal } = useToggle();
|
||||
|
||||
@@ -33,8 +49,26 @@ function OrderTrackingPage() {
|
||||
selectedPosition,
|
||||
selectedAddress,
|
||||
initialPhone: user?.number || '',
|
||||
editMode,
|
||||
addressData: address || null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editMode && address && !initialPositionSet) {
|
||||
const position: [number, number] = [address.latitude, address.longitude];
|
||||
setMapCenter(position);
|
||||
setSelectedPosition(position);
|
||||
setInitialPositionSet(true);
|
||||
|
||||
fetchAddressFromCoordinates(address.latitude, address.longitude).then((addr) => {
|
||||
if (addr) {
|
||||
setSelectedAddress(addr);
|
||||
setDetailsModal(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [editMode, address, initialPositionSet, setDetailsModal]);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeMarkers = async () => {
|
||||
const L = (await import('leaflet')).default;
|
||||
@@ -65,14 +99,16 @@ function OrderTrackingPage() {
|
||||
|
||||
const handlePositionSelect = (position: [number, number]) => {
|
||||
setSelectedPosition((prev) => {
|
||||
if (prev === position) {
|
||||
if (prev && prev[0] === position[0] && prev[1] === position[1]) {
|
||||
return prev;
|
||||
}
|
||||
setSelectedAddress(null);
|
||||
return position;
|
||||
});
|
||||
if (editMode && detailsModal) {
|
||||
setDetailsModal(false);
|
||||
}
|
||||
setConfirmModal(position && true);
|
||||
setDetailsModal(false);
|
||||
};
|
||||
|
||||
const handleZoomChange = (zoom: number) => {
|
||||
@@ -100,6 +136,20 @@ function OrderTrackingPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangePosition = () => {
|
||||
setDetailsModal(false);
|
||||
setSelectedPosition(null);
|
||||
setSelectedAddress(null);
|
||||
};
|
||||
|
||||
if (editMode && isLoadingAddress) {
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-50 flex items-center justify-center">
|
||||
<p className="text-sm2 text-disabled-text">در حال بارگذاری...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-gray-50">
|
||||
<div className="absolute inset-0">
|
||||
@@ -126,7 +176,9 @@ function OrderTrackingPage() {
|
||||
selectedAddress={selectedAddress}
|
||||
formData={formData}
|
||||
isPending={isPending}
|
||||
editMode={editMode}
|
||||
onClose={toggleDetailsModal}
|
||||
onChangePosition={handleChangePosition}
|
||||
onFormDataChange={handleFormDataChange}
|
||||
onSubmit={submitAddress}
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { api } from "@/lib/api/axiosInstance";
|
||||
import {
|
||||
CreateAddressType,
|
||||
AddressesResponse,
|
||||
AddressResponse,
|
||||
UpdateAddressType,
|
||||
} from "../types/Types";
|
||||
|
||||
@@ -17,7 +18,7 @@ export const createAddress = async (params: CreateAddressType) => {
|
||||
|
||||
export const updateAddress = async (params: UpdateAddressType) => {
|
||||
const { id, ...rest } = params;
|
||||
const { data } = await api.put(`/public/user/addresses/${id}`, rest);
|
||||
const { data } = await api.patch(`/public/user/addresses/${id}`, rest);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -30,3 +31,10 @@ export const setDefaultAddress = async (id: string) => {
|
||||
const { data } = await api.patch(`/public/user/addresses/${id}/default`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getAddressById = async (id: string): Promise<AddressResponse> => {
|
||||
const { data } = await api.get<AddressResponse>(
|
||||
`/public/user/addresses/${id}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,8 @@ export interface Address {
|
||||
|
||||
export type AddressesResponse = BaseResponse<Address[]>;
|
||||
|
||||
export type AddressResponse = BaseResponse<Address>;
|
||||
|
||||
export type CreateAddressType = {
|
||||
title: string;
|
||||
address: string;
|
||||
|
||||
Reference in New Issue
Block a user