mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
upload
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user