Spaces:
Running
Running
Update components/PropertiesPanel.tsx
Browse files
components/PropertiesPanel.tsx
CHANGED
|
@@ -1,31 +1,92 @@
|
|
| 1 |
|
|
|
|
| 2 |
import React from 'react';
|
| 3 |
import { Node } from 'reactflow';
|
| 4 |
-
import { NodeData, LayerDefinition } from '../types';
|
| 5 |
import { LAYER_DEFINITIONS } from '../constants';
|
| 6 |
-
import { X, Trash2 } from 'lucide-react';
|
| 7 |
|
| 8 |
interface PropertiesPanelProps {
|
| 9 |
selectedNode: Node<NodeData> | null;
|
| 10 |
onChange: (id: string, newData: Partial<NodeData>) => void;
|
| 11 |
onDelete: (id: string) => void;
|
| 12 |
onClose: () => void;
|
|
|
|
| 13 |
}
|
| 14 |
|
| 15 |
-
const PropertiesPanel: React.FC<PropertiesPanelProps> = ({ selectedNode, onChange, onDelete, onClose }) => {
|
| 16 |
if (!selectedNode) {
|
| 17 |
return (
|
| 18 |
-
<div className="w-80 bg-slate-900 border-l border-slate-800
|
| 19 |
-
<div className="
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
</div>
|
| 22 |
-
<p className="text-sm text-center">Select a node on the canvas to configure parameters.</p>
|
| 23 |
</div>
|
| 24 |
);
|
| 25 |
}
|
| 26 |
|
| 27 |
-
const definition: LayerDefinition = LAYER_DEFINITIONS[selectedNode.data.type];
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
const handleParamChange = (name: string, value: any, type: string) => {
|
| 30 |
let parsedValue = value;
|
| 31 |
if (type === 'number') parsedValue = Number(value);
|
|
@@ -141,4 +202,4 @@ const PropertiesPanel: React.FC<PropertiesPanelProps> = ({ selectedNode, onChang
|
|
| 141 |
);
|
| 142 |
};
|
| 143 |
|
| 144 |
-
export default PropertiesPanel;
|
|
|
|
| 1 |
|
| 2 |
+
|
| 3 |
import React from 'react';
|
| 4 |
import { Node } from 'reactflow';
|
| 5 |
+
import { NodeData, LayerDefinition, LogEntry } from '../types';
|
| 6 |
import { LAYER_DEFINITIONS } from '../constants';
|
| 7 |
+
import { X, Trash2, Activity, Info, CheckCircle, AlertTriangle, AlertOctagon } from 'lucide-react';
|
| 8 |
|
| 9 |
interface PropertiesPanelProps {
|
| 10 |
selectedNode: Node<NodeData> | null;
|
| 11 |
onChange: (id: string, newData: Partial<NodeData>) => void;
|
| 12 |
onDelete: (id: string) => void;
|
| 13 |
onClose: () => void;
|
| 14 |
+
logs?: LogEntry[];
|
| 15 |
}
|
| 16 |
|
| 17 |
+
const PropertiesPanel: React.FC<PropertiesPanelProps> = ({ selectedNode, onChange, onDelete, onClose, logs = [] }) => {
|
| 18 |
if (!selectedNode) {
|
| 19 |
return (
|
| 20 |
+
<div className="w-80 bg-slate-900 border-l border-slate-800 flex flex-col h-full">
|
| 21 |
+
<div className="p-4 border-b border-slate-800 flex items-center gap-2">
|
| 22 |
+
<Activity size={18} className="text-blue-400"/>
|
| 23 |
+
<h2 className="text-sm font-bold text-slate-200 uppercase tracking-wider">System Activity</h2>
|
| 24 |
+
</div>
|
| 25 |
+
|
| 26 |
+
<div className="flex-1 overflow-y-auto p-4 space-y-3 scrollbar-thin scrollbar-thumb-slate-700">
|
| 27 |
+
{logs.length === 0 ? (
|
| 28 |
+
<div className="flex flex-col items-center justify-center text-slate-500 h-64">
|
| 29 |
+
<div className="w-12 h-12 rounded-full bg-slate-800 mb-3 flex items-center justify-center">
|
| 30 |
+
<Activity size={24} className="opacity-20"/>
|
| 31 |
+
</div>
|
| 32 |
+
<p className="text-xs text-center">No activity recorded yet.</p>
|
| 33 |
+
</div>
|
| 34 |
+
) : (
|
| 35 |
+
logs.map(log => (
|
| 36 |
+
<div key={log.id} className="bg-slate-800/50 rounded border border-slate-800 p-3 animate-in fade-in slide-in-from-top-1 duration-300">
|
| 37 |
+
<div className="flex justify-between items-start mb-1">
|
| 38 |
+
<div className="flex items-center gap-2">
|
| 39 |
+
{log.type === 'info' && <Info size={12} className="text-blue-400" />}
|
| 40 |
+
{log.type === 'success' && <CheckCircle size={12} className="text-emerald-400" />}
|
| 41 |
+
{log.type === 'warning' && <AlertTriangle size={12} className="text-amber-400" />}
|
| 42 |
+
{log.type === 'error' && <AlertOctagon size={12} className="text-red-400" />}
|
| 43 |
+
<span className="text-[10px] font-bold text-slate-500 uppercase">{log.type}</span>
|
| 44 |
+
</div>
|
| 45 |
+
<span className="text-[10px] text-slate-600 font-mono">
|
| 46 |
+
{log.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
|
| 47 |
+
</span>
|
| 48 |
+
</div>
|
| 49 |
+
<p className="text-xs text-slate-300 leading-relaxed">{log.message}</p>
|
| 50 |
+
</div>
|
| 51 |
+
))
|
| 52 |
+
)}
|
| 53 |
+
|
| 54 |
+
<div className="pt-4 text-center">
|
| 55 |
+
<p className="text-[10px] text-slate-600">
|
| 56 |
+
Select a node on the canvas to configure parameters.
|
| 57 |
+
</p>
|
| 58 |
+
</div>
|
| 59 |
</div>
|
|
|
|
| 60 |
</div>
|
| 61 |
);
|
| 62 |
}
|
| 63 |
|
| 64 |
+
const definition: LayerDefinition | undefined = LAYER_DEFINITIONS[selectedNode.data.type];
|
| 65 |
|
| 66 |
+
if (!definition) {
|
| 67 |
+
return (
|
| 68 |
+
<div className="w-80 bg-slate-900 border-l border-slate-800 flex flex-col h-full">
|
| 69 |
+
<div className="p-4 border-b border-slate-800 flex justify-between items-center">
|
| 70 |
+
<h2 className="text-lg font-bold text-slate-100">Unknown Layer</h2>
|
| 71 |
+
<button onClick={onClose}><X size={18} className="text-slate-500 hover:text-white"/></button>
|
| 72 |
+
</div>
|
| 73 |
+
<div className="p-4 flex-1">
|
| 74 |
+
<div className="bg-red-500/10 border border-red-500/20 text-red-400 p-3 rounded text-sm mb-4">
|
| 75 |
+
Error: Layer definition not found for type "{selectedNode.data.type}".
|
| 76 |
+
This may happen if an imported template uses deprecated types.
|
| 77 |
+
</div>
|
| 78 |
+
<button
|
| 79 |
+
onClick={() => onDelete(selectedNode.id)}
|
| 80 |
+
className="w-full flex items-center justify-center gap-2 bg-red-500/10 hover:bg-red-500/20 text-red-500 py-2 rounded transition-colors text-sm font-medium border border-red-500/20"
|
| 81 |
+
>
|
| 82 |
+
<Trash2 size={16} />
|
| 83 |
+
Delete Node
|
| 84 |
+
</button>
|
| 85 |
+
</div>
|
| 86 |
+
</div>
|
| 87 |
+
);
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
const handleParamChange = (name: string, value: any, type: string) => {
|
| 91 |
let parsedValue = value;
|
| 92 |
if (type === 'number') parsedValue = Number(value);
|
|
|
|
| 202 |
);
|
| 203 |
};
|
| 204 |
|
| 205 |
+
export default PropertiesPanel;
|