- Introduced a new `LocationMap` component for handling latitude and longitude inputs in forms and detail views. - Updated form and detail components to utilize the `LocationMap`, enhancing user experience by allowing map-based location selection. - Added internationalization support for map-related labels in English and Indonesian. - Developed unit tests for the `LocationMap` to ensure functionality and reliability. This commit enhances the application by providing a user-friendly interface for managing geographical locations, improving data accuracy and user interaction.
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { CircleMarker, MapContainer, Polyline, TileLayer, Tooltip, useMap } from 'react-leaflet';
|
|
import { Box, Text } from '@mantine/core';
|
|
import type { RouteGeometry } from './route-geometry';
|
|
import { toLeafletLatLngs } from './route-geometry';
|
|
import { OSM_ATTRIBUTION, OSM_TILE_URL } from './osm';
|
|
import 'leaflet/dist/leaflet.css';
|
|
|
|
function FitRouteBounds({ positions }: { positions: Array<[number, number]> }) {
|
|
const map = useMap();
|
|
useEffect(() => {
|
|
if (positions.length === 0) return;
|
|
if (positions.length === 1) {
|
|
map.setView(positions[0], 14);
|
|
return;
|
|
}
|
|
map.fitBounds(positions, { padding: [24, 24] });
|
|
}, [map, positions]);
|
|
return null;
|
|
}
|
|
|
|
export interface RouteMapProps {
|
|
geometry?: RouteGeometry | null;
|
|
height?: number;
|
|
}
|
|
|
|
export function RouteMap({ geometry, height = 280 }: RouteMapProps) {
|
|
const positions = toLeafletLatLngs(geometry);
|
|
|
|
if (positions.length === 0) {
|
|
return (
|
|
<Box h={height} bdrs="md" bd="1px solid var(--mantine-color-default-border)" p="md">
|
|
<Text size="sm" c="dimmed">
|
|
No route geometry
|
|
</Text>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box h={height} bdrs="md" style={{ overflow: 'hidden' }}>
|
|
<MapContainer center={positions[0]} zoom={12} style={{ height: '100%', width: '100%' }} scrollWheelZoom>
|
|
<TileLayer attribution={OSM_ATTRIBUTION} url={OSM_TILE_URL} />
|
|
<Polyline positions={positions} pathOptions={{ color: 'var(--mantine-color-blue-6)', weight: 4 }} />
|
|
{positions.map((position, index) => (
|
|
<CircleMarker key={`${position[0]}-${position[1]}-${index}`} center={position} radius={8} pathOptions={{ color: 'var(--mantine-color-blue-8)' }}>
|
|
<Tooltip permanent>{index + 1}</Tooltip>
|
|
</CircleMarker>
|
|
))}
|
|
<FitRouteBounds positions={positions} />
|
|
</MapContainer>
|
|
</Box>
|
|
);
|
|
}
|