Files
MyClub/frontend/src/components/admin/MapLinkImporter.tsx
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

340 lines
10 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import {
Box,
Button,
FormControl,
FormLabel,
FormHelperText,
Input,
VStack,
HStack,
Text,
Alert,
AlertIcon,
AlertTitle,
AlertDescription,
Badge,
Divider,
Link,
useColorModeValue,
} from '@chakra-ui/react';
import MapStyleSelector from './MapStyleSelector';
import { FiMapPin, FiCheck, FiX, FiExternalLink } from 'react-icons/fi';
import { parseMapUrl, MapCoordinates, validateCoordinates, reverseGeocode } from '../../utils/mapUrlParser';
import ContactMap from '../home/ContactMap';
interface MapLinkImporterProps {
onImport: (coordinates: MapCoordinates) => void;
currentLatitude?: number;
currentLongitude?: number;
currentZoom?: number;
mapStyle?: string;
onMapStyleChange?: (style: string) => void;
clubPrimaryColor?: string;
clubSecondaryColor?: string;
clubName?: string;
}
const MapLinkImporter: React.FC<MapLinkImporterProps> = ({
onImport,
currentLatitude,
currentLongitude,
currentZoom,
mapStyle,
onMapStyleChange,
clubPrimaryColor,
clubSecondaryColor,
clubName,
}) => {
const [urlInput, setUrlInput] = useState('');
const [parsedData, setParsedData] = useState<MapCoordinates | null>(null);
const [error, setError] = useState<string | null>(null);
const [previewCoords, setPreviewCoords] = useState<MapCoordinates | null>(null);
const bgColor = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.700');
useEffect(() => {
// Initialize preview with current coordinates if available
if (currentLatitude && currentLongitude) {
setPreviewCoords({
latitude: currentLatitude,
longitude: currentLongitude,
zoom: currentZoom,
source: 'unknown',
});
}
}, [currentLatitude, currentLongitude, currentZoom]);
const handleUrlChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setUrlInput(value);
setError(null);
setParsedData(null);
if (!value.trim()) {
return;
}
// Try to parse the URL
const result = parseMapUrl(value);
if (result) {
if (validateCoordinates(result.latitude, result.longitude)) {
// Perform reverse geocoding to get detailed address
try {
const addressDetails = await reverseGeocode(result.latitude, result.longitude);
const enrichedResult = { ...result, ...addressDetails };
setParsedData(enrichedResult);
setPreviewCoords(enrichedResult);
setError(null);
} catch (err) {
// If geocoding fails, still use the basic data
setParsedData(result);
setPreviewCoords(result);
setError(null);
}
} else {
setError('Souřadnice jsou mimo platný rozsah');
setParsedData(null);
}
} else {
setError('Nepodařilo se rozpoznat URL mapy. Podporované: mapy.cz, Google Maps');
setParsedData(null);
}
};
const handleImport = () => {
if (parsedData) {
onImport(parsedData);
setUrlInput('');
setParsedData(null);
setError(null);
}
};
const handleClear = () => {
setUrlInput('');
setParsedData(null);
setError(null);
// Reset preview to current coordinates
if (currentLatitude && currentLongitude) {
setPreviewCoords({
latitude: currentLatitude,
longitude: currentLongitude,
zoom: currentZoom,
source: 'unknown',
});
} else {
setPreviewCoords(null);
}
};
return (
<VStack spacing={4} align="stretch">
<Box>
<FormControl>
<FormLabel display="flex" alignItems="center" gap={2}>
<FiMapPin /> Importovat z URL mapy
</FormLabel>
<Input
placeholder="Vložte URL z mapy.cz nebo Google Maps..."
value={urlInput}
onChange={handleUrlChange}
size="md"
/>
<FormHelperText>
Podporované formáty:
<Text as="span" fontWeight="semibold" ml={1}>mapy.cz</Text> (mapy.com/en/letecka?x=...&y=...),
<Text as="span" fontWeight="semibold" ml={1}>Google Maps</Text> (google.com/maps/place/@lat,lng,zoom)
</FormHelperText>
<HStack mt={2} spacing={3} fontSize="sm">
<Text color="gray.600">Quick links:</Text>
<Link
href="https://mapy.com/cs/"
isExternal
color="blue.500"
display="flex"
alignItems="center"
gap={1}
_hover={{ color: 'blue.600', textDecoration: 'underline' }}
>
Mapy.cz <FiExternalLink size={12} />
</Link>
<Text color="gray.400"></Text>
<Link
href="https://www.google.com/maps/"
isExternal
color="blue.500"
display="flex"
alignItems="center"
gap={1}
_hover={{ color: 'blue.600', textDecoration: 'underline' }}
>
Google Maps <FiExternalLink size={12} />
</Link>
</HStack>
</FormControl>
{parsedData && (
<Alert status="success" mt={3} borderRadius="md">
<AlertIcon />
<Box flex="1">
<AlertTitle>Úspěšně rozpoznáno!</AlertTitle>
<AlertDescription display="block">
<VStack align="start" spacing={1} mt={2}>
<HStack>
<Badge colorScheme="green">
{parsedData.source === 'mapy.cz' ? 'Mapy.cz' : 'Google Maps'}
</Badge>
</HStack>
<Text fontSize="sm">
<strong>Šířka:</strong> {parsedData.latitude.toFixed(7)}
</Text>
<Text fontSize="sm">
<strong>Délka:</strong> {parsedData.longitude.toFixed(7)}
</Text>
{parsedData.zoom && (
<Text fontSize="sm">
<strong>Zoom:</strong> {parsedData.zoom}
</Text>
)}
{parsedData.street && (
<Text fontSize="sm">
<strong>Ulice:</strong> {parsedData.street}
</Text>
)}
{parsedData.city && (
<Text fontSize="sm">
<strong>Město:</strong> {parsedData.city}
</Text>
)}
{parsedData.zip && (
<Text fontSize="sm">
<strong>PSČ:</strong> {parsedData.zip}
</Text>
)}
{parsedData.country && (
<Text fontSize="sm">
<strong>Země:</strong> {parsedData.country}
</Text>
)}
{parsedData.address && (
<Text fontSize="sm">
<strong>Celá adresa:</strong> {parsedData.address}
</Text>
)}
</VStack>
</AlertDescription>
</Box>
<HStack ml={2}>
<Button
leftIcon={<FiCheck />}
colorScheme="green"
size="sm"
onClick={handleImport}
>
Importovat
</Button>
<Button
leftIcon={<FiX />}
variant="ghost"
size="sm"
onClick={handleClear}
>
Zrušit
</Button>
</HStack>
</Alert>
)}
{error && (
<Alert status="error" mt={3} borderRadius="md">
<AlertIcon />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
</Box>
{/* Map Preview */}
{previewCoords && (
<>
<Divider />
<Box>
<Text fontWeight="semibold" mb={2}>
Náhled mapy
</Text>
<Box
borderRadius="md"
overflow="hidden"
borderWidth="1px"
borderColor={borderColor}
>
<ContactMap
latitude={previewCoords.latitude}
longitude={previewCoords.longitude}
zoom={previewCoords.zoom || 15}
address={previewCoords.address}
clubName={clubName}
mapStyle={mapStyle || 'positron'}
clubPrimaryColor={clubPrimaryColor}
clubSecondaryColor={clubSecondaryColor}
height={300}
/>
</Box>
<Text fontSize="xs" color="gray.500" mt={2}>
Souřadnice: {previewCoords.latitude.toFixed(6)}, {previewCoords.longitude.toFixed(6)}
{previewCoords.zoom && ` | Zoom: ${previewCoords.zoom}`}
</Text>
</Box>
{/* Map Style Selector */}
{onMapStyleChange && (
<>
<Divider />
<Box>
<Text fontWeight="semibold" mb={2}>
Styl mapy
</Text>
<Text fontSize="sm" color="gray.600" mb={3}>
Vyberte vzhled mapy, který se zobrazí na vašem webu.
</Text>
<MapStyleSelector
value={mapStyle || 'positron'}
onChange={onMapStyleChange}
clubPrimaryColor={clubPrimaryColor}
clubSecondaryColor={clubSecondaryColor}
showPreview={false}
/>
</Box>
</>
)}
</>
)}
{/* Example URLs */}
<Box
bg={bgColor}
p={3}
borderRadius="md"
borderWidth="1px"
borderColor={borderColor}
fontSize="sm"
>
<Text fontWeight="semibold" mb={2}>Příklady podporovaných URL:</Text>
<VStack align="start" spacing={1}>
<Text fontSize="xs" color="gray.600">
<strong>Mapy.cz:</strong><br />
mapy.cz/en/letecka?x=17.6996859&y=50.0947150&z=19
</Text>
<Text fontSize="xs" color="gray.600">
<strong>Google Maps:</strong><br />
google.com/maps/place/@50.0948669,17.7001456,226m
</Text>
</VStack>
</Box>
</VStack>
);
};
export default MapLinkImporter;