back icon in new address

This commit is contained in:
hamid zarghami
2025-12-25 11:14:00 +03:30
parent 0f2f0d5c29
commit e438d35b48
2 changed files with 125 additions and 8 deletions
@@ -13,6 +13,7 @@ import { useAddressForm } from './hooks/useAddressForm';
import { fetchAddressFromCoordinates } from './utils/fetchAddress'; import { fetchAddressFromCoordinates } from './utils/fetchAddress';
import { useGetAddressById } from '../hooks/useAddressData'; import { useGetAddressById } from '../hooks/useAddressData';
import { useGetAbout } from '@/app/[name]/(Main)/about/hooks/useAboutData'; import { useGetAbout } from '@/app/[name]/(Main)/about/hooks/useAboutData';
import { ActiveSearchbox } from '@/components/input/ActiveSearchbox';
const CustomMap = dynamic( const CustomMap = dynamic(
() => import('@/components/map/CustomMap'), () => import('@/components/map/CustomMap'),
@@ -34,6 +35,10 @@ function OrderTrackingPage() {
const [selectedAddress, setSelectedAddress] = useState<NominatimReverseGeocodingResponse | null>(null); const [selectedAddress, setSelectedAddress] = useState<NominatimReverseGeocodingResponse | null>(null);
const [markers, setMarkers] = useState<MarkerData[]>([]); const [markers, setMarkers] = useState<MarkerData[]>([]);
const [initialPositionSet, setInitialPositionSet] = useState(false); const [initialPositionSet, setInitialPositionSet] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const [searchResults, setSearchResults] = useState<Array<{ id: string; title: string; position: [number, number] }>>([]);
const [mapInstance, setMapInstance] = useState<L.Map | null>(null);
const [isSearchOpen, setIsSearchOpen] = useState(false);
const restaurant = aboutData?.data; const restaurant = aboutData?.data;
@@ -158,6 +163,72 @@ function OrderTrackingPage() {
setSelectedAddress(null); setSelectedAddress(null);
}; };
const [searchTimeout, setSearchTimeout] = useState<NodeJS.Timeout>();
const handleSearch = async (value: string) => {
if (!value || value.trim().length === 0) {
setSearchResults([]);
setIsSearchOpen(false);
return;
}
if (searchTimeout) {
clearTimeout(searchTimeout);
}
setIsSearching(true);
setIsSearchOpen(true);
const timeout = setTimeout(async () => {
try {
const viewbox = '44.0,25.0,63.0,40.0';
const response = await fetch(
`https://nominatim.openstreetmap.org/search?` +
`format=json&` +
`q=${encodeURIComponent(value)}&` +
`countrycodes=ir&` +
`viewbox=${viewbox}&` +
`bounded=0&` +
`limit=10&` +
`namedetails=1&` +
`accept-language=fa`
, {
headers: {
'Accept-Language': 'fa,en;q=0.9'
}
});
interface SearchResult {
place_id: string;
display_name: string;
lat: string;
lon: string;
}
const data: SearchResult[] = await response.json();
setSearchResults(data.map((item) => ({
id: item.place_id,
title: item.display_name,
position: [parseFloat(item.lat), parseFloat(item.lon)] as [number, number]
})));
} catch (error) {
console.error('Search error:', error);
setSearchResults([]);
} finally {
setIsSearching(false);
}
}, 500);
setSearchTimeout(timeout);
};
const handleSearchResultSelect = (result: { id: string; title: string; position: [number, number] }) => {
setSelectedPosition(result.position);
setMapCenter(result.position);
setIsSearchOpen(false);
if (mapInstance) {
mapInstance.setView(result.position, mapInstance.getZoom());
}
};
if (editMode && isLoadingAddress) { if (editMode && isLoadingAddress) {
return ( return (
<div className="fixed inset-0 bg-gray-50 flex items-center justify-center"> <div className="fixed inset-0 bg-gray-50 flex items-center justify-center">
@@ -185,20 +256,57 @@ function OrderTrackingPage() {
onPositionSelect={handlePositionSelect} onPositionSelect={handlePositionSelect}
onZoomChange={handleZoomChange} onZoomChange={handleZoomChange}
onClick={handleMapClick} onClick={handleMapClick}
searchEnabled={false}
onMapReady={setMapInstance}
/> />
</div> </div>
<div className="absolute top-4 right-4 z-1000"> <div className="absolute top-4 left-4 right-4 z-1001">
<div className="flex items-start gap-2" dir="ltr">
{!isSearchOpen && (
<button <button
onClick={() => router.back()} onClick={() => router.back()}
className="p-2 rounded-full bg-container/90 backdrop-blur-sm shadow-sm" className="p-2 rounded-full bg-container/90 backdrop-blur-sm shadow-sm shrink-0"
>
<ArrowLeft size={24} className="stroke-foreground" />
</button>
)}
<div className="flex-1 min-w-0 relative">
<div className="[&_.map-search-container]:static! [&_.map-search-container]:transform-none! [&_.map-search-container]:w-full! [&_.map-search-container]:max-w-none! [&_.map-search-container]:top-0!" dir="rtl">
<ActiveSearchbox
placeholder="جستجو"
onSearch={handleSearch}
isLoading={isSearching}
results={searchResults}
onResultSelect={(v) => {
const result = searchResults.find(r => r.id === v.id && r.title === v.title);
if (result) {
handleSearchResultSelect(result);
}
}}
onFocus={() => setIsSearchOpen(true)}
className="[&_div]:h-9! [&_input]:text-xs! [&_div]:px-3! [&_input]:px-2! [&_div]:dir-rtl! [&_input]:text-right! [&_input]:dir-rtl!"
/>
</div>
{isSearchOpen && (isSearching || searchResults.length > 0) && (
<div className="mt-2 flex items-start gap-2" dir="ltr">
<button
onClick={() => {
setIsSearchOpen(false);
setSearchResults([]);
}}
className="p-2 rounded-full bg-container/90 backdrop-blur-sm shadow-sm shrink-0 mt-1"
> >
<ArrowLeft size={24} className="stroke-foreground" /> <ArrowLeft size={24} className="stroke-foreground" />
</button> </button>
</div> </div>
)}
</div>
</div>
</div>
{hasServiceArea && ( {hasServiceArea && (
<div className="absolute top-4 left-4 z-1000 max-w-sm"> <div className="absolute bottom-4 left-4 z-1000 max-w-sm">
<div className="bg-blue-50/90 border border-blue-200 rounded-lg p-3 shadow-sm"> <div className="bg-blue-50/90 border border-blue-200 rounded-lg p-3 shadow-sm">
<p className="text-xs text-blue-800"> <p className="text-xs text-blue-800">
محدوده سرویسدهی روی نقشه با رنگ آبی مشخص شده است محدوده سرویسدهی روی نقشه با رنگ آبی مشخص شده است
+9
View File
@@ -28,6 +28,7 @@ interface CustomMapProps {
onClick?: (event: L.LeafletMouseEvent) => void; onClick?: (event: L.LeafletMouseEvent) => void;
searchEnabled?: boolean searchEnabled?: boolean
markerActive?: boolean markerActive?: boolean
onMapReady?: (map: L.Map) => void;
} }
// Component to handle map events and position updates // Component to handle map events and position updates
@@ -114,6 +115,7 @@ const CustomMap: React.FC<CustomMapProps> = ({
onClick, onClick,
markerActive = true, markerActive = true,
searchEnabled = true, searchEnabled = true,
onMapReady,
}) => { }) => {
const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null); const [selectedPosition, setSelectedPosition] = useState<[number, number] | null>(null);
const [mapInstance, setMapInstance] = useState<L.Map | null>(null); const [mapInstance, setMapInstance] = useState<L.Map | null>(null);
@@ -136,6 +138,13 @@ const CustomMap: React.FC<CustomMapProps> = ({
checkTiles(); checkTiles();
}, [mapInstance]); }, [mapInstance]);
// Notify parent when map is ready
useEffect(() => {
if (mapInstance && onMapReady) {
onMapReady(mapInstance);
}
}, [mapInstance, onMapReady]);
// Initialize default icon on client side only // Initialize default icon on client side only
useEffect(() => { useEffect(() => {
// Dynamic import of Leaflet on the client side // Dynamic import of Leaflet on the client side