chore(web): tighten event handler types

This commit is contained in:
root 2026-05-07 05:04:30 +00:00
parent 416adb134e
commit eef63cbb8f
7 changed files with 44 additions and 39 deletions

View File

@ -1,4 +1,5 @@
import { useState, useRef, useEffect, useMemo } from 'react';
import type { ChangeEvent } from 'react';
import { createPortal } from 'react-dom';
import { tradingRuntime } from '../lib/runtime';
import { getPlatformAccessToken } from '../lib/authSession';
@ -561,7 +562,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
<div className="text-[10px] text-[var(--muted-foreground)] uppercase tracking-wider font-bold">Edit Parameters Before Apply</div>
<Input
value={activeProfileData?.name || ''}
onChange={(e) => updateDraftField(msg.id, 'name', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraftField(msg.id, 'name', e.target.value)}
placeholder="Profile Name"
className="w-full rounded-lg px-2.5 py-1.5 text-[11px] outline-none"
style={inputStyle}
@ -572,7 +573,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
min="0"
step="1"
value={activeProfileData?.allocated_capital ?? ''}
onChange={(e) => updateDraftField(msg.id, 'allocated_capital', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraftField(msg.id, 'allocated_capital', e.target.value)}
placeholder="Capital"
className="w-full rounded-lg px-2.5 py-1.5 text-[11px] outline-none"
style={inputStyle}
@ -582,7 +583,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
min="0"
step="0.1"
value={activeProfileData?.risk_per_trade_percent ?? ''}
onChange={(e) => updateDraftField(msg.id, 'risk_per_trade_percent', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraftField(msg.id, 'risk_per_trade_percent', e.target.value)}
placeholder="Risk %"
className="w-full rounded-lg px-2.5 py-1.5 text-[11px] outline-none"
style={inputStyle}
@ -590,7 +591,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
</div>
<Input
value={activeProfileData?.symbols || ''}
onChange={(e) => updateDraftField(msg.id, 'symbols', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraftField(msg.id, 'symbols', e.target.value)}
placeholder="Symbols (e.g. BTC/USDT,ETH/USDT)"
className="w-full rounded-lg px-2.5 py-1.5 text-[11px] outline-none"
style={inputStyle}
@ -599,7 +600,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
<span className="text-[10px] text-[var(--muted-foreground)] uppercase tracking-wider">Auto Trading</span>
<Select
value={activeProfileData?.is_active === false ? 'false' : 'true'}
onChange={(e) => updateDraftField(msg.id, 'is_active', e.target.value === 'true')}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraftField(msg.id, 'is_active', e.target.value === 'true')}
className="rounded px-2 py-1 text-[10px] outline-none"
style={inputStyle}
options={[
@ -709,11 +710,11 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
border: '1px solid var(--border)',
cursor: 'pointer',
}}
onMouseEnter={e => {
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.borderColor = 'var(--ring)';
e.currentTarget.style.background = 'var(--accent-soft)';
}}
onMouseLeave={e => {
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
e.currentTarget.style.borderColor = 'var(--border)';
e.currentTarget.style.background = 'var(--card)';
}}
@ -755,7 +756,7 @@ export const ChatControl = ({ profiles, onApplyProfile }: ChatControlProps) => {
<Textarea
ref={inputRef}
value={input}
onChange={e => setInput(e.target.value)}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Describe a strategy profile..."
disabled={isLoading}

View File

@ -56,7 +56,7 @@ export function Login() {
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
placeholder="trader@example.com"
required
/>
@ -68,7 +68,7 @@ export function Login() {
label="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPassword(e.target.value)}
placeholder="••••••••"
required
/>

View File

@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import type { ChangeEvent } from 'react';
import { resetPlatformPassword } from '../lib/authSession';
import { Button, Input } from './ui/Primitives';
@ -50,7 +51,7 @@ export function ResetPassword() {
label="New Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setPassword(e.target.value)}
placeholder="New password"
required
/>

View File

@ -3,6 +3,7 @@
* Lets users compose IF/THEN trading rules without writing code.
*/
import { useState, useCallback, useEffect, useRef } from 'react';
import type { ChangeEvent } from 'react';
import {
DndContext,
closestCenter,
@ -136,7 +137,7 @@ function RuleCard({
controlSize="sm"
variant="surface"
options={(Object.keys(INDICATOR_LABELS) as Indicator[]).map(k => ({ value: k, label: INDICATOR_LABELS[k] }))}
onChange={e => onChange(rule.id, {
onChange={(e: ChangeEvent<HTMLSelectElement>) => onChange(rule.id, {
indicator: e.target.value as Indicator,
value: INDICATOR_DEFAULTS[e.target.value as Indicator],
})}
@ -149,7 +150,7 @@ function RuleCard({
controlSize="sm"
variant="surface"
options={(Object.keys(CONDITION_LABELS) as Condition[]).map(k => ({ value: k, label: CONDITION_LABELS[k] }))}
onChange={e => onChange(rule.id, { condition: e.target.value as Condition })}
onChange={(e: ChangeEvent<HTMLSelectElement>) => onChange(rule.id, { condition: e.target.value as Condition })}
/>
{/* Value */}
@ -159,7 +160,7 @@ function RuleCard({
style={numInp}
controlSize="sm"
variant="surface"
onChange={e => onChange(rule.id, { value: parseFloat(e.target.value) || 0 })}
onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(rule.id, { value: parseFloat(e.target.value) || 0 })}
/>
{/* THEN */}
@ -179,7 +180,7 @@ function RuleCard({
{ value: 'BUY', label: 'BUY' },
{ value: 'SELL', label: 'SELL' },
]}
onChange={e => onChange(rule.id, { action: e.target.value as TradeAction })}
onChange={(e: ChangeEvent<HTMLSelectElement>) => onChange(rule.id, { action: e.target.value as TradeAction })}
/>
{/* Quantity */}
@ -190,7 +191,7 @@ function RuleCard({
min={1}
controlSize="sm"
variant="surface"
onChange={e => onChange(rule.id, { quantity: parseFloat(e.target.value) || 1 })}
onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(rule.id, { quantity: parseFloat(e.target.value) || 1 })}
/>
{/* Qty type */}
@ -203,7 +204,7 @@ function RuleCard({
{ value: 'shares', label: 'shares' },
{ value: 'percent', label: '% of capital' },
]}
onChange={e => onChange(rule.id, { quantityType: e.target.value as QtyType })}
onChange={(e: ChangeEvent<HTMLSelectElement>) => onChange(rule.id, { quantityType: e.target.value as QtyType })}
/>
{/* Delete */}
@ -315,7 +316,7 @@ export function VisualRuleBuilder({ symbol, onSave, onBacktest }: Props) {
<div style={{ fontSize: 11, color: 'var(--muted-foreground)', fontWeight: 500, marginBottom: 3 }}>Strategy name</div>
<Input
value={name}
onChange={e => setName(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setName(e.target.value)}
controlSize="sm"
variant="surface"
style={{

View File

@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import type { ChangeEvent } from 'react';
import type { BotState } from '../hooks/useWebSocket';
import { getPlatformAccessToken } from '../lib/authSession';
import { tradingRuntime } from '../lib/runtime';
@ -1596,7 +1597,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<th className="px-4 py-2">
<Select
value={selectedProfileId}
onChange={(e) => setSelectedProfileId(e.target.value)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => setSelectedProfileId(e.target.value)}
options={[
{ value: 'all', label: 'All Profiles' },
...profiles.map((profileOption) => ({
@ -1614,7 +1615,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<Input
type="date"
value={ordersDateFrom}
onChange={(e) => setOrdersDateFrom(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setOrdersDateFrom(e.target.value)}
controlSize="sm"
variant="muted"
className="bg-white/5 border border-white/10 rounded px-2 py-1 text-[10px] text-gray-200 focus:outline-none focus:ring-2 focus:ring-orange-500/40"
@ -1622,7 +1623,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<Input
type="date"
value={ordersDateTo}
onChange={(e) => setOrdersDateTo(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setOrdersDateTo(e.target.value)}
controlSize="sm"
variant="muted"
className="bg-white/5 border border-white/10 rounded px-2 py-1 text-[10px] text-gray-200 focus:outline-none focus:ring-2 focus:ring-orange-500/40"
@ -1863,7 +1864,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<th className="px-4 py-2">
<Select
value={selectedProfileId}
onChange={(e) => setSelectedProfileId(e.target.value)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => setSelectedProfileId(e.target.value)}
options={[
{ value: 'all', label: 'All Profiles' },
...profiles.map((profileOption) => ({
@ -1884,7 +1885,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<Input
type="date"
value={lifecycleDateFrom}
onChange={(e) => setLifecycleDateFrom(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setLifecycleDateFrom(e.target.value)}
controlSize="sm"
variant="muted"
className="bg-white/5 border border-white/10 rounded px-2 py-1 text-[10px] text-gray-200 focus:outline-none focus:ring-2 focus:ring-cyan-500/40"
@ -1892,7 +1893,7 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
<Input
type="date"
value={lifecycleDateTo}
onChange={(e) => setLifecycleDateTo(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setLifecycleDateTo(e.target.value)}
controlSize="sm"
variant="muted"
className="bg-white/5 border border-white/10 rounded px-2 py-1 text-[10px] text-gray-200 focus:outline-none focus:ring-2 focus:ring-cyan-500/40"

View File

@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react';
import type { ChangeEvent } from 'react';
import { SlidersHorizontal, Search, RefreshCw } from 'lucide-react';
import { useAppContext } from '../context/AppContext';
import { useNavigate } from 'react-router-dom';
@ -187,14 +188,14 @@ export function ScreenerView() {
type="text"
placeholder="Filter by name or ticker…"
value={query}
onChange={e => setQuery(e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)}
style={{ paddingLeft: 32 }}
/>
</div>
<Select
value={String(capIdx)}
onChange={e => setCapIdx(Number(e.target.value))}
onChange={(e: ChangeEvent<HTMLSelectElement>) => setCapIdx(Number(e.target.value))}
style={{ width: 180 }}
options={CAP_OPTIONS.map((c, i) => ({ value: String(i), label: c.label }))}
/>
@ -222,7 +223,7 @@ export function ScreenerView() {
<Select
aria-label="More sectors"
value={SECTORS.indexOf(sector) >= 6 ? sector : ''}
onChange={e => e.target.value && setSector(e.target.value)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => e.target.value && setSector(e.target.value)}
options={[
{ value: '', label: 'More sectors…' },
...SECTORS.slice(6).map(s => ({ value: s, label: s })),

View File

@ -1,5 +1,5 @@
import { useEffect, useMemo, useReducer, useRef, useState } from 'react';
import type { FormEvent } from 'react';
import type { ChangeEvent, FormEvent } from 'react';
import { Pencil, RefreshCw, Trash2 } from 'lucide-react';
import { useSearchParams } from 'react-router-dom';
import { useAppContext } from '../context/AppContext';
@ -1044,7 +1044,7 @@ export function SimpleView() {
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Existing holding</span>
<Select
value={selectedHoldingTradeId || ''}
onChange={(e) => {
onChange={(e: ChangeEvent<HTMLSelectElement>) => {
const selected = availableSellHoldings.find((holding) => (holding.tradeId || '') === e.target.value);
if (selected) applyHoldingToDraft(selected);
}}
@ -1067,7 +1067,7 @@ export function SimpleView() {
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Symbol</span>
<Input
value={draft.symbol}
onChange={(e) => {
onChange={(e: ChangeEvent<HTMLInputElement>) => {
dispatch({ type: 'set-market-price-source', value: null });
if (draft.side === 'sell') {
dispatch({ type: 'set-selected-holding-trade-id', value: null });
@ -1136,7 +1136,7 @@ export function SimpleView() {
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Setup type</span>
<Select
value={draft.side}
onChange={(e) => updateDraft('side', e.target.value as SimpleSide)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('side', e.target.value as SimpleSide)}
options={[
{ value: 'buy', label: 'Buy the dip + profit exit' },
{ value: 'sell', label: 'Manage existing holding at profit' },
@ -1192,7 +1192,7 @@ export function SimpleView() {
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Sizing method</span>
<Select
value={draft.sizingMode}
onChange={(e) => updateDraft('sizingMode', e.target.value as 'quantity' | 'amount')}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('sizingMode', e.target.value as 'quantity' | 'amount')}
options={[
{ value: 'quantity', label: 'Quantity / fractional shares' },
{ value: 'amount', label: 'USD amount' },
@ -1206,7 +1206,7 @@ export function SimpleView() {
</span>
<Input
value={draft.sizingMode === 'amount' ? draft.amountUsd : draft.quantity}
onChange={(e) => {
onChange={(e: ChangeEvent<HTMLInputElement>) => {
if (draft.sizingMode === 'amount') {
updateDraft('amountUsd', e.target.value);
} else {
@ -1230,7 +1230,7 @@ export function SimpleView() {
</span>
<Input
value={draft.side === 'sell' && selectedSellHolding ? String(selectedSellHolding.size) : draft.quantity}
onChange={(e) => updateDraft('quantity', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraft('quantity', e.target.value)}
readOnly={draft.side === 'sell' && !!selectedSellHolding}
className="read-only:bg-[var(--muted)]"
placeholder="10"
@ -1242,7 +1242,7 @@ export function SimpleView() {
<span className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Notes</span>
<Input
value={draft.notes}
onChange={(e) => updateDraft('notes', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraft('notes', e.target.value)}
placeholder="Optional context"
/>
</label>
@ -1254,7 +1254,7 @@ export function SimpleView() {
<p className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Drop trigger</p>
<Select
value={draft.dropMode}
onChange={(e) => updateDraft('dropMode', e.target.value as TriggerMode)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('dropMode', e.target.value as TriggerMode)}
options={[
{ value: 'dollar', label: 'Dollar drop from current market' },
{ value: 'percent', label: 'Percent drop from current market' },
@ -1267,7 +1267,7 @@ export function SimpleView() {
</span>
<Input
value={draft.dropValue}
onChange={(e) => updateDraft('dropValue', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraft('dropValue', e.target.value)}
placeholder={draft.dropMode === 'dollar' ? '0.00' : '0'}
/>
</label>
@ -1279,7 +1279,7 @@ export function SimpleView() {
<p className="text-[11px] font-black uppercase tracking-[0.24em] text-zinc-700">Profit exit</p>
<Select
value={draft.profitMode}
onChange={(e) => updateDraft('profitMode', e.target.value as TriggerMode)}
onChange={(e: ChangeEvent<HTMLSelectElement>) => updateDraft('profitMode', e.target.value as TriggerMode)}
options={[
{ value: 'dollar', label: 'Dollar gain from purchase' },
{ value: 'percent', label: 'Percent gain from purchase' },
@ -1292,7 +1292,7 @@ export function SimpleView() {
</span>
<Input
value={draft.profitValue}
onChange={(e) => updateDraft('profitValue', e.target.value)}
onChange={(e: ChangeEvent<HTMLInputElement>) => updateDraft('profitValue', e.target.value)}
placeholder={draft.profitMode === 'dollar' ? '7.50' : '10'}
/>
</label>