import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { IconArrowLeft, IconColumnInsertRight, IconTicket, IconAlertTriangle } from '@tabler/icons-react'
import { PageHeader, Card, Button, Badge } from '../components/ui'
import RoadmapBoard from '../components/roadmap/RoadmapBoard'
import NodeDetailPanel from '../components/roadmap/NodeDetailPanel'
import RoadmapProgressBar from '../components/roadmap/RoadmapProgressBar'
import { useAuth } from '../context/AuthContext'
import { useRoadmaps } from '../hooks/useRoadmaps'
import { useRoadmapNodes } from '../hooks/useRoadmapNodes'
import { databases, DATABASE_ID, COLLECTIONS } from '../lib/appwrite'
import { computeProgress, NODE_KIND, ROADMAP_STATUS } from '../lib/roadmaps'
export default function RoadmapDetailPage() {
const { id } = useParams()
const navigate = useNavigate()
const { user } = useAuth()
const { fetchById, updateRoadmap, completePlan } = useRoadmaps()
const {
nodes, fetchNodes, createNode, updateNode, deleteNode,
connectNodes, disconnectNodes, setNodeStatus,
} = useRoadmapNodes()
const [roadmap, setRoadmap] = useState(null)
const [ticketStatus, setTicketStatus] = useState(null)
const [selectedNode, setSelectedNode] = useState(null)
const [notFound, setNotFound] = useState(false)
const load = useCallback(async () => {
const doc = await fetchById(id)
if (!doc) {
setNotFound(true)
return
}
setRoadmap(doc)
await fetchNodes(doc.$id)
if (doc.ticketId) {
try {
const ticket = await databases.getDocument(DATABASE_ID, COLLECTIONS.WORKORDERS, doc.ticketId)
setTicketStatus(ticket.status)
} catch {
setTicketStatus(null)
}
}
}, [id, fetchById, fetchNodes])
useEffect(() => { load() }, [load])
// Teamarbeit: bei Fenster-Fokus neu laden (einfacher Schutz gegen veraltete Ansicht)
useEffect(() => {
const onFocus = () => load()
window.addEventListener('focus', onFocus)
return () => window.removeEventListener('focus', onFocus)
}, [load])
// Ausgewaehlten Knoten mit frischen Daten aus der Liste spiegeln
useEffect(() => {
if (!selectedNode) return
const fresh = nodes.find((n) => n.$id === selectedNode.$id)
if (!fresh) setSelectedNode(null)
else if (fresh !== selectedNode) setSelectedNode(fresh)
}, [nodes]) // eslint-disable-line react-hooks/exhaustive-deps
if (notFound) {
return (
navigate('/roadmaps')}> Zur Uebersicht
)
}
if (!roadmap) {
return
}
const readOnly = roadmap.status === ROADMAP_STATUS.COMPLETED
const progress = computeProgress(nodes)
const ticketClosedButActive = !readOnly && ticketStatus === 'Closed'
const handleAddNode = async (column, kind, rowCount) => {
const result = await createNode(roadmap.$id, {
title: kind === NODE_KIND.GOAL ? 'Neues Ziel' : 'Neuer Schritt',
column,
row: rowCount,
kind,
})
if (result.success) setSelectedNode(result.data)
else window.alert(result.error)
}
const handleConnect = async (source, target) => {
const result = await connectNodes(source, target)
if (!result.success) window.alert(result.error)
}
const handleSaveNode = async (node, data) => updateNode(node.$id, data)
const handleSetStatus = async (node, status) => {
const result = await setNodeStatus(roadmap, node, status, user)
if (!result.success) {
window.alert(result.error)
return
}
if (result.allGoalsDone) {
const ok = window.confirm(
'Alle Ziel-Knoten sind fertig!\n\n' +
'Plan abschliessen und archivieren? Das Projektticket wird dabei geschlossen.'
)
if (ok) {
const done = await completePlan(roadmap, user)
if (done.success) setRoadmap(done.data)
else window.alert(`Abschliessen fehlgeschlagen: ${done.error}`)
}
}
}
const handleDeleteNode = async (node) => {
if (!window.confirm(`Knoten "${node.title}" loeschen?`)) return
const result = await deleteNode(node)
if (!result.success) window.alert(result.error)
else setSelectedNode(null)
}
const handleDisconnect = async (sourceId, target) => {
const result = await disconnectNodes(sourceId, target)
if (!result.success) window.alert(result.error)
}
/** Neue Phase vor den Zielen einfuegen: Ziel-Knoten ruecken eine Spalte nach rechts. */
const handleAddColumn = async () => {
const newCount = (roadmap.columnCount || 3) + 1
if (newCount > 50) return
const goals = nodes.filter((n) => n.kind === NODE_KIND.GOAL)
for (const goal of goals) {
await updateNode(goal.$id, { column: newCount - 1 })
}
const result = await updateRoadmap(roadmap.$id, { columnCount: newCount })
if (result.success) setRoadmap(result.data)
else window.alert(result.error)
}
return (
{readOnly && Abgeschlossen }
{roadmap.woid && (
Ticket {roadmap.woid}
)}
{!readOnly && (
Spalte
)}
navigate('/roadmaps')}> Zurueck
}
/>
{ticketClosedButActive && (
Das Projektticket wurde manuell geschlossen, der Plan ist aber erst zu {progress.percent}% fertig.
Statusaenderungen werden weiterhin im Ticketverlauf dokumentiert.
)}
{selectedNode && (
setSelectedNode(null)}
onSave={handleSaveNode}
onSetStatus={handleSetStatus}
onDelete={handleDeleteNode}
onDisconnect={handleDisconnect}
/>
)}
)
}