Files
robinhood/frontend/src/components/IndicatorPanel.tsx
T

158 lines
5.8 KiB
TypeScript

import { useState } from 'react';
import { Settings, Check, X } from 'lucide-react';
interface IndicatorConfig {
id: string;
name: string;
enabled: boolean;
color: string;
params: Record<string, number>;
}
interface IndicatorPanelProps {
indicators: IndicatorConfig[];
onChange: (indicators: IndicatorConfig[]) => void;
}
export default function IndicatorPanel({ indicators, onChange }: IndicatorPanelProps) {
const [isOpen, setIsOpen] = useState(false);
const toggleIndicator = (id: string) => {
const updated = indicators.map((ind) =>
ind.id === id ? { ...ind, enabled: !ind.enabled } : ind
);
onChange(updated);
};
const updateParam = (id: string, param: string, value: number) => {
// Validate input
if (isNaN(value) || value <= 0) {
return; // Don't update with invalid values
}
const updated = indicators.map((ind) =>
ind.id === id ? { ...ind, params: { ...ind.params, [param]: value } } : ind
);
onChange(updated);
};
return (
<div className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-3 py-2 bg-dark-surface rounded-md hover:bg-dark-hover transition-colors"
>
<Settings className="w-4 h-4" />
<span className="text-sm">Indicators</span>
<span className="text-xs bg-blue-600 px-2 py-0.5 rounded-full">
{indicators.filter((i) => i.enabled).length}
</span>
</button>
{isOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-40"
onClick={() => setIsOpen(false)}
/>
{/* Panel */}
<div className="absolute top-full mt-2 right-0 w-80 bg-dark-surface border border-dark-border rounded-lg shadow-xl z-50">
<div className="p-4">
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold">Technical Indicators</h3>
<button
onClick={() => setIsOpen(false)}
className="p-1 hover:bg-dark-hover rounded"
>
<X className="w-4 h-4" />
</button>
</div>
<div className="space-y-3 max-h-96 overflow-y-auto">
{indicators.map((indicator) => (
<div
key={indicator.id}
className="p-3 bg-dark-bg rounded-lg border border-dark-border"
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: indicator.color }}
/>
<span className="font-medium text-sm">{indicator.name}</span>
</div>
<button
onClick={() => toggleIndicator(indicator.id)}
className={`
p-1 rounded transition-colors
${
indicator.enabled
? 'bg-green-600 text-white'
: 'bg-gray-700 text-gray-400'
}
`}
>
{indicator.enabled ? (
<Check className="w-4 h-4" />
) : (
<X className="w-4 h-4" />
)}
</button>
</div>
{indicator.enabled && Object.keys(indicator.params).length > 0 && (
<div className="space-y-2 mt-3 pt-3 border-t border-dark-border">
{Object.entries(indicator.params).map(([param, value]) => (
<div key={param}>
<label className="block text-xs text-gray-400 mb-1">
{param.charAt(0).toUpperCase() + param.slice(1)}
</label>
<input
type="number"
value={value}
onChange={(e) =>
updateParam(indicator.id, param, Number(e.target.value))
}
min="1"
step="1"
className="w-full px-2 py-1 text-sm bg-dark-bg border border-dark-border rounded focus:ring-2 focus:ring-blue-500 focus:outline-none"
/>
</div>
))}
</div>
)}
</div>
))}
</div>
<div className="mt-4 pt-4 border-t border-dark-border">
<button
onClick={() => {
const allEnabled = indicators.map((ind) => ({ ...ind, enabled: true }));
onChange(allEnabled);
}}
className="w-full px-3 py-2 bg-blue-600 hover:bg-blue-700 rounded text-sm font-medium transition-colors"
>
Enable All
</button>
<button
onClick={() => {
const allDisabled = indicators.map((ind) => ({ ...ind, enabled: false }));
onChange(allDisabled);
}}
className="w-full mt-2 px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm font-medium transition-colors"
>
Disable All
</button>
</div>
</div>
</div>
</>
)}
</div>
);
}