import { useEffect, useState } from 'react' import { PlusCircle, Trash2, Info, AlertTriangle } from 'lucide-react' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { Slider } from '@/components/ui/slider' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { ScrollArea } from '@/components/ui/scroll-area' import { Switch } from '@/components/ui/switch' import { Separator } from '@/components/ui/separator' import { useProcessStore } from '@/store/process-store' import { formatCurrency } from '@/lib/format' import type { Activity } from '@/domain/types' interface ActivityPanelProps { selectedElementId: string | null } export function ActivityPanel({ selectedElementId }: ActivityPanelProps) { const { activities, resources, updateActivity } = useProcessStore() const [localActivity, setLocalActivity] = useState(null) const [isDirty, setIsDirty] = useState(false) const activity = activities.find((a) => a.bpmnElementId === selectedElementId) ?? null // Sincronizar con el store cuando cambia la selección useEffect(() => { setLocalActivity(activity ? { ...activity, assignedResources: [...activity.assignedResources] } : null) setIsDirty(false) }, [selectedElementId, activities]) function handleChange(key: K, value: Activity[K]) { if (!localActivity) return setLocalActivity((prev) => prev ? { ...prev, [key]: value } : null) setIsDirty(true) } async function handleSave() { if (!localActivity) return await updateActivity(localActivity) setIsDirty(false) } function addResource(resourceId: string) { if (!localActivity) return const already = localActivity.assignedResources.some((r) => r.resourceId === resourceId) if (already) return handleChange('assignedResources', [ ...localActivity.assignedResources, { resourceId, utilizationPercent: 1.0 }, ]) } function removeResource(resourceId: string) { if (!localActivity) return handleChange( 'assignedResources', localActivity.assignedResources.filter((r) => r.resourceId !== resourceId) ) } function setUtilization(resourceId: string, value: number) { if (!localActivity) return handleChange( 'assignedResources', localActivity.assignedResources.map((r) => r.resourceId === resourceId ? { ...r, utilizationPercent: value } : r ) ) } if (!selectedElementId) { return (

Seleccioná una actividad

Hacé click sobre una tarea en el diagrama para editar sus atributos de costo

) } if (!localActivity) { return (

Esta actividad no tiene configuración todavía

) } const availableResources = resources.filter( (r) => !localActivity.assignedResources.some((ar) => ar.resourceId === r.id) ) return (
{/* Nombre (read-only, viene del BPMN) */}

{localActivity.name || localActivity.bpmnElementId}

{localActivity.bpmnElementId}

{/* Costo directo fijo */}
Costo fijo por cada ejecución de esta actividad (materiales, licencias, etc.) — independiente del tiempo
handleChange('directCostFixed', Math.max(0, parseFloat(e.target.value) || 0))} className="font-mono h-8 text-sm" placeholder="0" />
{/* Tiempo de ejecución */}
Tiempo promedio estimado para completar esta actividad
handleChange('executionTimeMinutes', Math.max(0, parseFloat(e.target.value) || 0))} className="font-mono h-8 text-sm" placeholder="0" />
{/* Recursos asignados */}
{localActivity.assignedResources.length === 0 ? (

Sin recursos asignados

) : (
{localActivity.assignedResources.map((assignment) => { const resource = resources.find((r) => r.id === assignment.resourceId) if (!resource) return null const hourlyShare = (resource.costPerHour * assignment.utilizationPercent).toFixed(2) return (

{resource.name}

{formatCurrency(resource.costPerHour)}/h · {formatCurrency(parseFloat(hourlyShare))}/h efectivo

Utilización {Math.round(assignment.utilizationPercent * 100)}%
setUtilization(assignment.resourceId, v / 100)} />
) })}
)} {/* Agregar recurso */} {availableResources.length > 0 && ( )} {resources.length === 0 && (

Creá recursos en la tab "Recursos" para asignarlos

)}
{/* ── Sección: Automatización ──────────────────────────────────── */}

Automatización

{/* Toggle */}
handleChange('automatable', v)} aria-label="Marcar actividad como automatizable" />
{/* Campos — aparecen con transición suave cuando toggle=ON */}
{/* Costo automatizado */}
Costo recurrente por ejecución automatizada. No incluye la inversión inicial del proyecto. Ver METHODOLOGY_AUTOMATED_COST.md para la guía de cálculo.
handleChange('automatedCostFixed', Math.max(0, parseFloat(e.target.value) || 0))} className="font-mono h-8 text-sm" placeholder="0" />
{/* Tiempo automatizado */}
handleChange('automatedTimeMinutes', Math.max(0, parseFloat(e.target.value) || 0))} className="font-mono h-8 text-sm" placeholder="0" />
{/* Helper ámbar — siempre en el DOM para que la transición funcione al tipear un valor (no desaparece instantáneo sino con fade). La condición controla opacity + max-h, no mount/unmount. */} {(() => { const showHelper = localActivity.automatable && localActivity.automatedCostFixed === 0 && localActivity.automatedTimeMinutes === 0 return (

Esta actividad aparecerá en "Transparencia metodológica" del reporte hasta que cargues valores.

) })()}
{/* Guardar */}
) }