import { useEffect, useState, useCallback } from 'react' import type { OrganizationalUnit, EmployeeUnitAssignment } from '@skillmate/shared' import api from '../services/api' import { useAuthStore } from '../stores/authStore' interface OrganizationChartProps { onClose: () => void } export default function OrganizationChart({ onClose }: OrganizationChartProps) { const [units, setUnits] = useState([]) const [hierarchy, setHierarchy] = useState([]) const [selectedUnit, setSelectedUnit] = useState(null) const [unitEmployees, setUnitEmployees] = useState([]) const [myUnits, setMyUnits] = useState([]) const [searchTerm, setSearchTerm] = useState('') const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [message, setMessage] = useState('') const [zoomLevel, setZoomLevel] = useState(1) const [panPosition, setPanPosition] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) const { user } = useAuthStore() useEffect(() => { loadOrganization() loadMyUnits() }, []) const loadOrganization = async () => { try { const response = await api.get('/organization/hierarchy') if (response.data.success) { setHierarchy(response.data.data) } } catch (error) { console.error('Failed to load organization:', error) } finally { setLoading(false) } } const loadMyUnits = async () => { try { const response = await api.get('/organization/my-units') if (response.data.success) { setMyUnits(response.data.data) } } catch (error) { console.error('Failed to load my units:', error) } } const loadUnitDetails = async (unitId: string) => { try { const response = await api.get(`/organization/units/${unitId}`) if (response.data.success) { setSelectedUnit(response.data.data) setUnitEmployees(response.data.data.employees || []) } } catch (error) { console.error('Failed to load unit details:', error) } } const assignMeToSelectedUnit = async () => { if (!selectedUnit || !user?.employeeId) return setSaving(true) setMessage('') try { await api.post('/organization/assignments', { employeeId: user.employeeId, unitId: selectedUnit.id, role: 'mitarbeiter', isPrimary: true }) setMessage('Position erfolgreich gesetzt.') await loadMyUnits() await loadUnitDetails(selectedUnit.id) } catch (error: any) { const msg = error?.response?.data?.error?.message || 'Zuweisung fehlgeschlagen' setMessage(`Fehler: ${msg}`) } finally { setSaving(false) setTimeout(() => setMessage(''), 3000) } } const getTypeIcon = (type: string) => { const icons: Record = { direktion: 'πŸ›οΈ', abteilung: '🏒', dezernat: 'πŸ“', sachgebiet: 'πŸ“‹', teildezernat: 'πŸ”§', fuehrungsstelle: '⭐', stabsstelle: '🎯', sondereinheit: 'πŸ›‘οΈ' } return icons[type] || 'πŸ“„' } const getUnitColor = (level: number) => { const colors = [ 'bg-gradient-to-r from-purple-500 to-purple-700', 'bg-gradient-to-r from-pink-500 to-pink-700', 'bg-gradient-to-r from-blue-500 to-blue-700', 'bg-gradient-to-r from-green-500 to-green-700', 'bg-gradient-to-r from-orange-500 to-orange-700', 'bg-gradient-to-r from-teal-500 to-teal-700' ] return colors[level % colors.length] } const handleUnitClick = (unit: OrganizationalUnit) => { setSelectedUnit(unit) loadUnitDetails(unit.id) } const handleZoomIn = () => setZoomLevel(prev => Math.min(prev + 0.2, 3)) const handleZoomOut = () => setZoomLevel(prev => Math.max(prev - 0.2, 0.3)) const handleResetView = () => { setZoomLevel(1) setPanPosition({ x: 0, y: 0 }) } const handleMouseDown = (e: React.MouseEvent) => { if (e.button === 0) { // Left click only setIsDragging(true) setDragStart({ x: e.clientX - panPosition.x, y: e.clientY - panPosition.y }) } } const handleMouseMove = (e: React.MouseEvent) => { if (isDragging) { setPanPosition({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y }) } } const handleMouseUp = () => { setIsDragging(false) } const renderUnit = (unit: any, level: number = 0) => { const isMyUnit = myUnits.some(u => u.id === unit.id) const isSearchMatch = searchTerm && unit.name.toLowerCase().includes(searchTerm.toLowerCase()) return (
handleUnitClick(unit)} className={` cursor-pointer transform transition-all duration-200 hover:scale-105 ${isMyUnit ? 'ring-4 ring-yellow-400' : ''} ${isSearchMatch ? 'ring-4 ring-blue-400 animate-pulse' : ''} `} >
{getTypeIcon(unit.type)} {unit.code && {unit.code}}

{unit.name}

{unit.hasFuehrungsstelle && ( FΓΌSt )}
{unit.children && unit.children.length > 0 && (
{unit.children.map((child: any) => (
{renderUnit(child, level + 1)}
))}
)}
) } if (loading) { return (
Lade Organigramm...
) } return (
{/* Main Modal */}
{/* Left Sidebar */}

Navigation

{/* Search */}
setSearchTerm(e.target.value)} className="w-full px-3 py-2 border rounded dark:bg-gray-700 dark:border-gray-600 dark:text-white" />
{/* Quick Links */}
{/* Department Filters */}

Abteilungen

{[1, 2, 3, 4, 5, 6].map(n => ( ))}
{/* Main Chart Area */}
{/* Header */}

LKA NRW Organigramm

{/* Zoom Controls */}
{/* Chart Canvas */}
{hierarchy.map(unit => renderUnit(unit))}
{/* Right Sidebar - Details */} {selectedUnit && (

{selectedUnit.name}

{selectedUnit.code &&

{selectedUnit.code}

}
{/* Tabs */}
{/* Employee List */}
{unitEmployees.map(emp => (
{emp.photo ? ( ) : (
πŸ‘€
)}

{emp.firstName} {emp.lastName}

{emp.role === 'leiter' && 'πŸ”‘ Leitung'} {emp.role === 'stellvertreter' && '↔️ Stellvertretung'} {emp.role === 'mitarbeiter' && 'Mitarbeitende Person'} {emp.role === 'beauftragter' && 'πŸŽ–οΈ Beauftragter'}

))} {unitEmployees.length === 0 && (

Keine Mitarbeitenden zugeordnet

)}
{selectedUnit.description && (

Beschreibung

{selectedUnit.description}

)} {user?.employeeId && (
{message && (

{message}

)}
)}
)}
) }