From 2d6551fd35e3aba8ff88f259ceeb6078215894cd Mon Sep 17 00:00:00 2001 From: Henry Winkel Date: Sat, 16 May 2026 21:01:08 +0200 Subject: [PATCH] fix: improve TradingSettings validation, debounce, accessibility, and cleanup --- app/components/TradingSettings.tsx | 56 +++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/app/components/TradingSettings.tsx b/app/components/TradingSettings.tsx index 36409c4..5255533 100644 --- a/app/components/TradingSettings.tsx +++ b/app/components/TradingSettings.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; interface TradingSettingsProps { settings: Record; @@ -13,6 +13,8 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading const [stopLossPercent, setStopLossPercent] = useState(settings["trading.stopLossPercent"] ?? 3); const [riskMethod, setRiskMethod] = useState(settings["trading.riskMethod"] ?? "percentage"); + const saveTimersRef = useRef>>(new Map()); + useEffect(() => { setMaxLossPercent(settings["trading.maxLossPercent"] ?? 2); setPositionSizePercent(settings["trading.positionSizePercent"] ?? 10); @@ -21,10 +23,25 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading setRiskMethod(settings["trading.riskMethod"] ?? "percentage"); }, [settings]); - const save = async (key: string, value: any) => { - await onSave(key, value); + const debouncedSave = (key: string, value: any) => { + if (saveTimersRef.current.has(key)) { + clearTimeout(saveTimersRef.current.get(key)!); + } + const timer = setTimeout(() => { + onSave(key, value); + saveTimersRef.current.delete(key); + }, 300); + saveTimersRef.current.set(key, timer); }; + useEffect(() => { + return () => { + saveTimersRef.current.forEach((timer) => clearTimeout(timer)); + }; + }, []); + + const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max); + return (
@@ -38,17 +55,18 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading
- + { - const v = parseFloat(e.target.value) || 0; + const v = clamp(parseFloat(e.target.value) || 0.1, 0.1, 100); setMaxLossPercent(v); - save("trading.maxLossPercent", v); + debouncedSave("trading.maxLossPercent", v); }} className="w-32 border border-gray-300 rounded-lg px-4 py-2.5 text-gray-900 focus:ring-2 focus:ring-blue-500" /> @@ -56,17 +74,18 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading
- + { - const v = parseInt(e.target.value) || 1; + const v = clamp(parseInt(e.target.value) || 1, 1, 100); setPositionSizePercent(v); - save("trading.positionSizePercent", v); + debouncedSave("trading.positionSizePercent", v); }} className="w-32 border border-gray-300 rounded-lg px-4 py-2.5 text-gray-900 focus:ring-2 focus:ring-blue-500" /> @@ -74,17 +93,18 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading
- + { - const v = parseFloat(e.target.value) || 0; + const v = clamp(parseFloat(e.target.value) || 0.1, 0.1, 100); setTakeProfitPercent(v); - save("trading.takeProfitPercent", v); + debouncedSave("trading.takeProfitPercent", v); }} className="w-32 border border-gray-300 rounded-lg px-4 py-2.5 text-gray-900 focus:ring-2 focus:ring-blue-500" /> @@ -92,17 +112,18 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading
- + { - const v = parseFloat(e.target.value) || 0; + const v = clamp(parseFloat(e.target.value) || 0.1, 0.1, 100); setStopLossPercent(v); - save("trading.stopLossPercent", v); + debouncedSave("trading.stopLossPercent", v); }} className="w-32 border border-gray-300 rounded-lg px-4 py-2.5 text-gray-900 focus:ring-2 focus:ring-blue-500" /> @@ -110,12 +131,13 @@ export default function TradingSettings({ settings, onSave, saveError }: Trading
- +