import { useState } from 'react'; import type { ProcessNode } from '../../types/process'; import type { Stream } from '../../types/stream'; import StreamEditor from './StreamEditor'; import { useUIStore } from '../../store/uiStore'; import './SubprocessCard.css'; interface Props { id?: string; subprocess: ProcessNode; subIdx: number; groupIdx: number; allProcessNames: string[]; onUpdate: (updated: ProcessNode) => void; onDelete: () => void; onPlaceRequest: (target: string) => void; placementTarget: string | null; } const DEFAULT_STREAM: Stream = { name: 'Stream 1', type: 'product', display_vars: ['Tin', 'Tout', 'ṁ', 'cp'], stream_values: {}, }; export default function SubprocessCard({ id, subprocess, subIdx, groupIdx: _groupIdx, allProcessNames, onUpdate, onDelete, onPlaceRequest, placementTarget, }: Props) { const expanded = useUIStore((s) => s.expandedSubprocesses.has(subIdx)); const setExpanded = (v: boolean) => useUIStore.getState().setSubprocessExpanded(subIdx, v); const activeSection = useUIStore((s) => s.activeSections[subIdx] || 'streams'); const setActiveSection = (sec: string) => useUIStore.getState().setActiveSection(subIdx, sec); const [confirmDel, setConfirmDel] = useState(false); const isPlacing = placementTarget === `sub_${subIdx}`; const update = (partial: Partial) => { onUpdate({ ...subprocess, ...partial }); }; const addStream = () => { const streams = [...(subprocess.streams || [])]; streams.push({ ...DEFAULT_STREAM, name: `Stream ${streams.length + 1}`, }); update({ streams }); }; const updateStream = (sIdx: number, updated: Stream) => { const streams = [...(subprocess.streams || [])]; streams[sIdx] = updated; update({ streams }); }; const deleteStream = (sIdx: number) => { const streams = (subprocess.streams || []).filter((_, i) => i !== sIdx); update({ streams }); }; // Build next options: all other process names const nextOptions = allProcessNames.filter((n) => n !== subprocess.name); const selectedNext = subprocess.next ? subprocess.next.split(',').map((s) => s.trim()).filter(Boolean) : []; return (
{/* Header */}
update({ name: e.target.value })} />
update({ box_scale: parseFloat(e.target.value) }) } />
{confirmDel ? ( ) : ( )}
{/* Expanded body */} {expanded && (
{/* Section tabs */}
{[ { key: 'relationships', label: 'Relationships', color: '#2980b9' }, { key: 'streams', label: 'Streams', color: '#e74c3c' }, { key: 'notes', label: 'Notes', color: '#f39c12' }, ].map((tab) => ( ))}
{activeSection === 'relationships' && (
{nextOptions.length === 0 && No other processes available.} {nextOptions.map((opt) => { const isSelected = selectedNext.includes(opt); return ( ); })}
)} {/* Streams */} {activeSection === 'streams' && (
Streams ({subprocess.streams?.length || 0})
{(subprocess.streams || []).map((stream, sIdx) => ( updateStream(sIdx, updated)} onDelete={() => deleteStream(sIdx)} /> ))}
)} {/* Parameters & Notes */} {activeSection === 'notes' && (
update({ extra_info: { ...(subprocess.extra_info || {}), density: e.target.value, }, }) } />
update({ extra_info: { ...(subprocess.extra_info || {}), pressure: e.target.value, }, }) } />
update({ extra_info: { ...(subprocess.extra_info || {}), water_content_in: e.target.value, }, }) } />
update({ extra_info: { ...(subprocess.extra_info || {}), water_content_out: e.target.value, }, }) } />