Files
dmenu-plus-front/src/app/[name]/(Profile)/profile/address/new/page.tsx
T
2025-08-09 15:10:02 +03:30

270 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useSearchParams } from 'next/navigation';
import React, { useState, useEffect } from 'react';
import type { Icon } from 'leaflet';
import dynamic from 'next/dynamic';
import AnimatedBottomSheet from '@/components/bottomsheet/AnimatedBottomSheet';
import Button from '@/components/button/PrimaryButton';
import InputField from '@/components/input/InputField';
import { Skeleton } from '@/components/ui/skeleton';
const CustomMap = dynamic(
() => import('@/components/map/CustomMap'),
{ ssr: false } // Leaflet requires browser APIs
);
export interface MarkerData {
position: [number, number];
title: string;
icon?: Icon;
}
export interface NominatimReverseGeocodingResponse {
place_id: number
licence: string
osm_type: string
osm_id: number
lat: string
lon: string
class: string
type: string
place_rank: number
importance: number
addresstype: string
name: string
display_name: string
address: NominatimReverseGeocodingResponseAddress
boundingbox: string[]
}
export interface NominatimReverseGeocodingResponseAddress {
road: string
neighbourhood: string
suburb: string
city: string
district: string
county: string
province: string
"ISO3166-2-lvl4": string
postcode: string
country: string
state: string
country_code: string
}
function OrderTrackingPage() {
const params = useSearchParams();
const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null);
const [selectedAddress, setSelectedAddress] = useState<NominatimReverseGeocodingResponse | null>(null);
const [markers, setMarkers] = useState<MarkerData[]>([]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [mapCenter, setMapCenter] = useState<[number, number]>([Number(params?.get('lon')) || 35.6892, Number(params?.get('lon')) || 51.3890]);
const [bottomSheet1State, setBottomSheet1State] = useState<boolean>(false);
const [bottomSheet2State, setBottomSheet2State] = useState<boolean>(false);
// Example initial position (Tehran)
const initialPosition = React.useMemo<[number, number]>(() => [35.6892, 51.3890], []);
useEffect(() => {
// Initialize markers on the client side
const initializeMarkers = async () => {
// Dynamic import of Leaflet on the client side
const L = (await import('leaflet')).default;
setMarkers([
{
position: initialPosition,
title: 'Restaurant Location',
icon: new L.Icon({
iconUrl: '/icons/restaurant-marker.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
})
}
]);
};
initializeMarkers();
}, [initialPosition]);
const handlePositionSelect = (position: [number, number]) => {
setSelectedPosition((prev) => {
if (prev === position) {
return prev;
}
setSelectedAddress(null);
return position;
});
setBottomSheet1State(position && true);
setBottomSheet2State(false);
// You can handle the selected position here
console.log('Selected position:', position);
};
const handleZoomChange = (zoom: number) => {
console.log('Zoom level:', zoom);
};
const handleMapClick = (e: L.LeafletMouseEvent) => {
console.log('Map clicked at:', e.latlng);
};
const toggleBottomSheet1 = () => {
setBottomSheet1State((prev) => !prev);
}
const toggleBottomSheet2 = async () => {
if (!selectedPosition) {
setBottomSheet1State(false);
setBottomSheet2State(false);
return;
}
setBottomSheet1State(false);
setBottomSheet2State((prev) => !prev);
if (!bottomSheet2State) {
const address = await fetchAddressFromCoordinates(selectedPosition[0], selectedPosition[1]);
setSelectedAddress(address);
}
}
const fetchAddressFromCoordinates = async (lat: unknown, lon: unknown) => {
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json&addressdetails=1&accept-language=fa`
);
const data = await response.json();
return data; // Full formatted address
} catch (error) {
console.error('Error fetching address:', error);
return null;
}
};
const submitAddressAction = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
console.log(formData);
}
return (
<div className="fixed inset-0 bg-gray-50">
<div className="absolute inset-0">
<CustomMap
initialPosition={mapCenter}
zoom={14}
markers={markers}
onPositionSelect={handlePositionSelect}
onZoomChange={handleZoomChange}
onClick={handleMapClick}
/>
</div>
{/* {selectedPosition && (
<div className="selected-location-indicator">
<p className="font-iran">موقعیت انتخاب شده: {selectedPosition[0].toFixed(4)}, {selectedPosition[1].toFixed(4)}</p>
</div>
)} */}
<div className="absolute bottom-0 left-0 right-0 z-[1000]">
<div className='relative w-full h-full' >
<AnimatedBottomSheet
bgOpacity={0}
inDuration={0}
blurOpacity={null}
noBlur
visible={bottomSheet1State}
showCloseButton={false}
showTitle={false}
overrideClassName='!pt-6 bg-container!'
>
<div className='grid grid-cols-2 gap-4 px-4'>
<Button
onClick={toggleBottomSheet2}
>
انتخاب
</Button>
<Button
onClick={toggleBottomSheet1}
className='bg-disabled! text-foreground!'
>
انصراف
</Button>
</div>
</AnimatedBottomSheet>
<AnimatedBottomSheet
bgOpacity={0}
inDuration={0}
blurOpacity={null}
noBlur
visible={bottomSheet2State}
title='آدرس'
onClick={toggleBottomSheet2}
overrideClassName='bg-container!'
>
<form onSubmit={submitAddressAction}>
<div className='px-4'>
<div className='px-4 mt-2 bg-container'>
<span className='text-sm'>
{!selectedAddress ?
<Skeleton
className='h-6 w-full'
/>
:
[
selectedAddress.address.state ?? selectedAddress.address.province,
selectedAddress.address.city,
selectedAddress.address.neighbourhood,
!selectedAddress.address.road.startsWith('میدان') &&
!selectedAddress.address.road.startsWith('بلوار') &&
!selectedAddress.address.road.startsWith('خیابان') &&
!selectedAddress.address.road.startsWith('کوچه') ?
selectedAddress.type === 'secondary' ? `بلوار ${selectedAddress.address.road}` :
selectedAddress.type === 'tertiary' ? `خیابان ${selectedAddress.address.road}` :
selectedAddress.type === 'residential' ? `کوچه ${selectedAddress.address.road}` :
selectedAddress.address.road : selectedAddress.address.road
].filter(x => x).join('، ')
}
</span>
<hr className='border border-border mt-3 mb-10 ' />
<InputField
htmlFor={'addressTitle'}
labelText={'عنوان آدرس'}
placeholder='مثال: منزل، محل کار ...'
className='bg-inherit'
onChange={() => { }}
/>
<InputField
htmlFor={'addressDetails'}
labelText={'جزئیات آدرس'}
placeholder='میدان باغ ملی، مجتمع آسمان، واحد 12، پلاک 1234567890'
className='bg-inherit mt-8'
onChange={() => { }}
/>
</div>
<div className='grid grid-cols-2 gap-4 mt-16'>
<Button
type='submit'>
تایید
</Button>
<Button
type='button'
onClick={toggleBottomSheet2}
className='bg-disabled! text-foreground!'
>
انصراف
</Button>
</div>
</div>
</form>
</AnimatedBottomSheet>
</div>
</div>
</div>
);
}
export default OrderTrackingPage;