frontend_nyla/src/components/UiComponents/ParcelInfoPanel/ParcelInfoPanel.tsx

275 lines
13 KiB
TypeScript

import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { FaTimes, FaTrash } from 'react-icons/fa';
import styles from './ParcelInfoPanel.module.css';
export interface ParcelInfoPanelProps {
isOpen: boolean;
onClose: () => void;
parcels: any[];
onRemoveParcel?: (parcelId: string) => void;
adjacentParcels?: any[];
}
const ParcelInfoPanel: React.FC<ParcelInfoPanelProps> = ({
isOpen,
onClose,
parcels,
onRemoveParcel,
adjacentParcels = []
}) => {
if (!parcels || parcels.length === 0) return null;
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className={styles.backdrop}
onClick={onClose}
/>
{/* Panel */}
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', damping: 30, stiffness: 300 }}
className={styles.panel}
>
<div className={styles.header}>
<h2 className={styles.title}>Parzellen-Informationen ({parcels.length})</h2>
<button className={styles.closeButton} onClick={onClose}>
<FaTimes />
</button>
</div>
<div className={styles.content}>
{/* Selected Parcels List */}
<div className={styles.parcelsList}>
{parcels.map((parcelData, index) => (
<section key={parcelData.parcel.id || index} className={styles.section}>
<div className={styles.sectionHeader}>
<h3 className={styles.sectionTitle}>
Parzelle {index + 1}: {parcelData.parcel.number || parcelData.parcel.id || 'Unbekannt'}
</h3>
{onRemoveParcel && (
<button
className={styles.removeButton}
onClick={() => onRemoveParcel(parcelData.parcel.id)}
title="Parzelle entfernen"
>
<FaTrash />
</button>
)}
</div>
<div className={styles.infoGrid}>
{parcelData.parcel.id && (
<div className={styles.infoItem}>
<span className={styles.label}>ID:</span>
<span className={styles.value}>{parcelData.parcel.id}</span>
</div>
)}
{parcelData.parcel.number && (
<div className={styles.infoItem}>
<span className={styles.label}>Nummer:</span>
<span className={styles.value}>{parcelData.parcel.number}</span>
</div>
)}
{parcelData.parcel.name && (
<div className={styles.infoItem}>
<span className={styles.label}>Name:</span>
<span className={styles.value}>{parcelData.parcel.name}</span>
</div>
)}
{parcelData.parcel.egrid && (
<div className={styles.infoItem}>
<span className={styles.label}>EGRID:</span>
<span className={styles.value}>{parcelData.parcel.egrid}</span>
</div>
)}
{parcelData.parcel.identnd && (
<div className={styles.infoItem}>
<span className={styles.label}>IdentND:</span>
<span className={styles.value}>{parcelData.parcel.identnd}</span>
</div>
)}
{parcelData.parcel.address && (
<div className={styles.infoItem}>
<span className={styles.label}>Adresse:</span>
<span className={styles.value}>{parcelData.parcel.address}</span>
</div>
)}
{parcelData.parcel.canton && (
<div className={styles.infoItem}>
<span className={styles.label}>Kanton:</span>
<span className={styles.value}>{parcelData.parcel.canton}</span>
</div>
)}
{parcelData.parcel.municipality_name && (
<div className={styles.infoItem}>
<span className={styles.label}>Gemeinde:</span>
<span className={styles.value}>{parcelData.parcel.municipality_name}</span>
</div>
)}
{parcelData.parcel.municipality_code && (
<div className={styles.infoItem}>
<span className={styles.label}>Gemeinde-Code:</span>
<span className={styles.value}>{parcelData.parcel.municipality_code}</span>
</div>
)}
{parcelData.parcel.area_m2 !== undefined && (
<div className={styles.infoItem}>
<span className={styles.label}>Fläche:</span>
<span className={styles.value}>
{parcelData.parcel.area_m2.toFixed(2)} m²
{parcelData.parcel.area_m2 >= 10000 && (
<span className={styles.subValue}>
{' '}({(parcelData.parcel.area_m2 / 10000).toFixed(2)} ha)
</span>
)}
</span>
</div>
)}
{parcelData.parcel.realestate_type && (
<div className={styles.infoItem}>
<span className={styles.label}>Grundstückstyp:</span>
<span className={styles.value}>{parcelData.parcel.realestate_type}</span>
</div>
)}
{parcelData.parcel.bauzone && (
<div className={styles.infoItem}>
<span className={styles.label}>Bauzone:</span>
<span className={styles.value}>{parcelData.parcel.bauzone}</span>
</div>
)}
{parcelData.parcel.zone && Array.isArray(parcelData.parcel.zone) && parcelData.parcel.zone.length > 0 && (
<div className={styles.infoItem}>
<span className={styles.label}>Zone:</span>
<span className={styles.value}>
{parcelData.parcel.zone.length} Zone{parcelData.parcel.zone.length !== 1 ? 'n' : ''} gefunden
{(() => {
// Extract zone types from zone array
const zoneTypes = parcelData.parcel.zone
.map((z: any) => {
const attrs = z.attributes || {};
return attrs.typ || attrs.zone_typ || attrs.bauzone || attrs.zone || attrs.label || null;
})
.filter((t: string | null) => t !== null);
if (zoneTypes.length > 0) {
return (
<span className={styles.subValue}>
{' '}({zoneTypes.join(', ')})
</span>
);
}
return null;
})()}
{import.meta.env.DEV && (
<details className={styles.zoneDetails}>
<summary className={styles.zoneSummary}>Details anzeigen</summary>
<pre className={styles.zoneData}>
{JSON.stringify(parcelData.parcel.zone, null, 2)}
</pre>
</details>
)}
</span>
</div>
)}
{parcelData.parcel.centroid && (
<div className={styles.infoItem}>
<span className={styles.label}>Zentrum (LV95):</span>
<span className={styles.value}>
{parcelData.parcel.centroid.x.toFixed(2)}, {parcelData.parcel.centroid.y.toFixed(2)}
</span>
</div>
)}
{parcelData.parcel.geoportal_url && (
<div className={styles.infoItem}>
<span className={styles.label}>Geoportal:</span>
<a
href={parcelData.parcel.geoportal_url}
target="_blank"
rel="noopener noreferrer"
className={styles.link}
>
Link öffnen
</a>
</div>
)}
</div>
{/* Map View Info for this parcel */}
{parcelData.map_view && (
<div className={styles.mapViewSection}>
<h4 className={styles.subSectionTitle}>Kartenansicht</h4>
<div className={styles.infoGrid}>
{parcelData.map_view.center && (
<div className={styles.infoItem}>
<span className={styles.label}>Zentrum:</span>
<span className={styles.value}>
{parcelData.map_view.center.x.toFixed(2)}, {parcelData.map_view.center.y.toFixed(2)}
</span>
</div>
)}
{parcelData.map_view.zoom_bounds && (
<>
<div className={styles.infoItem}>
<span className={styles.label}>Bounds Min:</span>
<span className={styles.value}>
{parcelData.map_view.zoom_bounds.min_x.toFixed(2)}, {parcelData.map_view.zoom_bounds.min_y.toFixed(2)}
</span>
</div>
<div className={styles.infoItem}>
<span className={styles.label}>Bounds Max:</span>
<span className={styles.value}>
{parcelData.map_view.zoom_bounds.max_x.toFixed(2)}, {parcelData.map_view.zoom_bounds.max_y.toFixed(2)}
</span>
</div>
</>
)}
</div>
</div>
)}
</section>
))}
</div>
{/* Adjacent Parcels */}
{adjacentParcels.length > 0 && (
<section className={styles.section}>
<h3 className={styles.sectionTitle}>
Angrenzende Parzellen ({adjacentParcels.length})
</h3>
<div className={styles.adjacentList}>
{adjacentParcels.map((adjacent, index) => (
<div key={adjacent.id || index} className={styles.adjacentItem}>
<div className={styles.adjacentHeader}>
<span className={styles.adjacentNumber}>
{adjacent.number || adjacent.id}
</span>
{adjacent.egrid && (
<span className={styles.adjacentEgrid}>{adjacent.egrid}</span>
)}
</div>
</div>
))}
</div>
</section>
)}
</div>
</motion.div>
</>
)}
</AnimatePresence>
);
};
export default ParcelInfoPanel;