- Add advanced metrics dashboard with trade analytics - Add new trading components (EntryTypeAnalysis, MultiDayPositionTracker, NewsEventTracker, etc.) - Add strategy mode selector and trend confirmation - Add risk automation panel and slippage correlation analysis - Add daily trading plan enhancements with modal components - Add custom hooks (useApi, useLocalStorage, useAdvancedTradeMetrics) - Add broker service integration and trading API - Add test setup and vitest configuration - Include parquet data files for live market data - Add comprehensive documentation in docs/ folder
581 lines
21 KiB
TypeScript
581 lines
21 KiB
TypeScript
import { useState, useCallback, useMemo, useEffect } from 'react';
|
|
import { useLocalStorage } from '@/hooks';
|
|
import { AlertModal, ConfirmModal } from '@/components/shared/Modal';
|
|
import { PlanHeader } from './PlanHeader';
|
|
import { PlanBiasSelector } from './PlanBiasSelector';
|
|
import { PlanRiskParameters } from './PlanRiskParameters';
|
|
import { PlanKeyLevelsEditor } from './PlanKeyLevelsEditor';
|
|
import { usePlanGeneration } from './usePlanGeneration';
|
|
import StrategyModeSelector from '@/components/StrategyModeSelector';
|
|
import TrendConfirmation from '@/components/TrendConfirmation';
|
|
import MultiDayPositionTracker from '@/components/MultiDayPositionTracker';
|
|
import NewsEventTracker from '@/components/NewsEventTracker';
|
|
import type { TradingPlan, DailyTradingPlanProps } from './types';
|
|
import type { StrategyMode } from '@/components/StrategyModeSelector';
|
|
import { STRATEGY_PRESETS } from '@/components/StrategyModeSelector';
|
|
import AdvancedMetricsDashboard, { type Trade as AdvancedMetricsTrade, type VolatilityBucket } from '@/components/AdvancedMetricsDashboard';
|
|
|
|
const createDefaultPlan = (currentPrice: number, strategyMode: StrategyMode = 'SWING'): TradingPlan => {
|
|
const preset = STRATEGY_PRESETS[strategyMode];
|
|
const riskAmount = 10000 * (preset.riskPerTrade / 100); // Assume $10k account
|
|
const stopLossDiff = currentPrice * (preset.stopLossPercent / 100);
|
|
const takeProfitDiff = currentPrice * (preset.takeProfitPercent / 100);
|
|
|
|
return {
|
|
date: new Date().toDateString(),
|
|
strategyMode,
|
|
bias: 'NEUTRAL',
|
|
dailyTarget: Math.round(riskAmount * 2), // 2x risk as daily target
|
|
maxLoss: Math.round(riskAmount),
|
|
entryZone: {
|
|
min: currentPrice - (currentPrice * (preset.stopLossPercent / 200)),
|
|
max: currentPrice + (currentPrice * (preset.stopLossPercent / 200))
|
|
},
|
|
targetPrice: currentPrice + takeProfitDiff,
|
|
stopLoss: currentPrice - stopLossDiff,
|
|
keyLevels: {
|
|
support: [
|
|
currentPrice - (currentPrice * (preset.stopLossPercent / 50)),
|
|
currentPrice - (currentPrice * (preset.stopLossPercent / 25))
|
|
],
|
|
resistance: [
|
|
currentPrice + (currentPrice * (preset.takeProfitPercent / 50)),
|
|
currentPrice + (currentPrice * (preset.takeProfitPercent / 25))
|
|
],
|
|
},
|
|
tradingNotes: '',
|
|
maxTrades: preset.maxDailyTrades,
|
|
actualTrades: 0,
|
|
actualPnL: 0,
|
|
planFollowed: true,
|
|
contextMetrics: null,
|
|
};
|
|
};
|
|
|
|
const SIGNAL_LABELS: Record<AdvancedMetricsTrade['signalType'], string> = {
|
|
RSI_CROSSOVER: 'RSI Crossover',
|
|
MA_CROSSOVER: 'MA Crossover',
|
|
BB_BREAKOUT: 'Bollinger Breakout',
|
|
MACD: 'MACD Signal',
|
|
SUPPORT_BOUNCE: 'Support Bounce',
|
|
TREND_CONFIRMATION: 'Trend Confirmation',
|
|
NEWS_TRIGGERED: 'News Triggered',
|
|
};
|
|
|
|
export default function DailyTradingPlan({
|
|
currentPrice,
|
|
onPlanUpdate,
|
|
openRouterReady,
|
|
openRouterMessage,
|
|
advancedTrades = [],
|
|
advancedTradesSource = 'sample',
|
|
}: DailyTradingPlanProps): JSX.Element {
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [showResetConfirm, setShowResetConfirm] = useState(false);
|
|
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
|
|
const [successMessage, setSuccessMessage] = useState('');
|
|
const [showAdvancedMetrics, setShowAdvancedMetrics] = useState(false);
|
|
const [analyticsFocus, setAnalyticsFocus] = useState<{
|
|
timeframe: string | null;
|
|
signalType: AdvancedMetricsTrade['signalType'] | null;
|
|
volatility: string | null;
|
|
}>({ timeframe: null, signalType: null, volatility: null });
|
|
|
|
const safeAdvancedTrades = advancedTrades ?? [];
|
|
const advancedAnalyticsSummary = useMemo(() => {
|
|
if (!safeAdvancedTrades.length) return null;
|
|
|
|
const totalTrades = safeAdvancedTrades.length;
|
|
const wins = safeAdvancedTrades.filter((trade) => trade.profitable).length;
|
|
const winRate = (wins / totalTrades) * 100;
|
|
|
|
const timeframePnL = safeAdvancedTrades.reduce<Record<string, number>>((acc, trade) => {
|
|
acc[trade.timeframe] = (acc[trade.timeframe] ?? 0) + trade.pnl;
|
|
return acc;
|
|
}, {});
|
|
const bestTimeframeEntry = Object.entries(timeframePnL).sort((a, b) => b[1] - a[1])[0];
|
|
|
|
const signalPnL = safeAdvancedTrades.reduce<Record<string, number>>((acc, trade) => {
|
|
acc[trade.signalType] = (acc[trade.signalType] ?? 0) + trade.pnl;
|
|
return acc;
|
|
}, {});
|
|
const bestSignalEntry = Object.entries(signalPnL).sort((a, b) => b[1] - a[1])[0];
|
|
|
|
const slippageImpact = safeAdvancedTrades.reduce((sum, trade) => {
|
|
const gross = trade.grossPnL ?? trade.pnl;
|
|
if (!gross) return sum;
|
|
const impact = trade.slippage ? (trade.slippage / Math.abs(gross)) * 100 : 0;
|
|
return sum + impact;
|
|
}, 0) / totalTrades;
|
|
|
|
return {
|
|
totalTrades,
|
|
winRate,
|
|
bestTimeframe: bestTimeframeEntry?.[0] ?? null,
|
|
bestSignal: (bestSignalEntry?.[0] as AdvancedMetricsTrade['signalType']) ?? null,
|
|
slippageImpact,
|
|
};
|
|
}, [safeAdvancedTrades]);
|
|
|
|
const [plan, setPlan] = useLocalStorage<TradingPlan>(
|
|
'daily-trading-plan',
|
|
createDefaultPlan(currentPrice)
|
|
);
|
|
|
|
const { generating, error, generatePlan } = usePlanGeneration(openRouterReady);
|
|
|
|
// Ensure plan resets when the stored date is stale (runs after render to avoid blocking updates)
|
|
useEffect(() => {
|
|
const today = new Date().toDateString();
|
|
if (plan.date !== today) {
|
|
setPlan((prev) => {
|
|
const next = createDefaultPlan(currentPrice, prev.strategyMode);
|
|
return {
|
|
...next,
|
|
bias: prev.bias,
|
|
tradingNotes: prev.tradingNotes,
|
|
};
|
|
});
|
|
}
|
|
}, [plan.date, currentPrice, setPlan]);
|
|
|
|
const handleEdit = useCallback(() => {
|
|
setIsEditing(true);
|
|
}, []);
|
|
|
|
const handleSave = useCallback(() => {
|
|
setIsEditing(false);
|
|
if (onPlanUpdate) {
|
|
onPlanUpdate(plan);
|
|
}
|
|
}, [plan, onPlanUpdate]);
|
|
|
|
const handleConfirmReset = useCallback(() => {
|
|
setPlan(createDefaultPlan(currentPrice));
|
|
setIsEditing(false);
|
|
setShowResetConfirm(false);
|
|
}, [currentPrice, setPlan]);
|
|
|
|
const handleGenerateAI = useCallback(async () => {
|
|
const generatedPlan = await generatePlan(currentPrice, plan);
|
|
|
|
if (generatedPlan) {
|
|
setPlan(generatedPlan);
|
|
setSuccessMessage(
|
|
`AI Plan Generated!\n\nBias: ${generatedPlan.bias}\nTarget: $${generatedPlan.dailyTarget}\n\nReview and edit the plan as needed.`
|
|
);
|
|
setShowSuccessAlert(true);
|
|
if (onPlanUpdate) {
|
|
onPlanUpdate(generatedPlan);
|
|
}
|
|
}
|
|
}, [currentPrice, plan, generatePlan, setPlan, onPlanUpdate]);
|
|
|
|
const handleFieldChange = useCallback(
|
|
(field: string, value: number) => {
|
|
setPlan((prev) => {
|
|
if (field === 'entryZoneMin') {
|
|
return { ...prev, entryZone: { ...prev.entryZone, min: value } };
|
|
}
|
|
if (field === 'entryZoneMax') {
|
|
return { ...prev, entryZone: { ...prev.entryZone, max: value } };
|
|
}
|
|
return { ...prev, [field]: value };
|
|
});
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleBiasChange = useCallback(
|
|
(bias: 'BULLISH' | 'BEARISH' | 'NEUTRAL') => {
|
|
setPlan((prev) => ({ ...prev, bias }));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleNotesChange = useCallback(
|
|
(notes: string) => {
|
|
setPlan((prev) => ({ ...prev, tradingNotes: notes }));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleAdvancedTimeframeSelect = useCallback((timeframe: string) => {
|
|
setAnalyticsFocus((prev) => ({
|
|
...prev,
|
|
timeframe: prev.timeframe === timeframe ? null : timeframe,
|
|
}));
|
|
}, []);
|
|
|
|
const handleAdvancedSignalSelect = useCallback((signalType: string) => {
|
|
const typedSignal = signalType as AdvancedMetricsTrade['signalType'];
|
|
setAnalyticsFocus((prev) => ({
|
|
...prev,
|
|
signalType: prev.signalType === typedSignal ? null : typedSignal,
|
|
}));
|
|
}, []);
|
|
|
|
const handleAdvancedVolatilitySelect = useCallback((bucket: VolatilityBucket) => {
|
|
setAnalyticsFocus((prev) => ({
|
|
...prev,
|
|
volatility: prev.volatility === bucket.range ? null : bucket.range,
|
|
}));
|
|
}, []);
|
|
|
|
const handleStrategyModeChange = useCallback(
|
|
(mode: StrategyMode) => {
|
|
const newPlan = createDefaultPlan(currentPrice, mode);
|
|
setPlan((prev) => ({
|
|
...newPlan,
|
|
bias: prev.bias,
|
|
tradingNotes: prev.tradingNotes,
|
|
}));
|
|
},
|
|
[currentPrice, setPlan]
|
|
);
|
|
|
|
// Key levels handlers
|
|
const handleAddSupport = useCallback(() => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
support: [...prev.keyLevels.support, currentPrice - 10],
|
|
},
|
|
}));
|
|
}, [currentPrice, setPlan]);
|
|
|
|
const handleAddResistance = useCallback(() => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
resistance: [...prev.keyLevels.resistance, currentPrice + 10],
|
|
},
|
|
}));
|
|
}, [currentPrice, setPlan]);
|
|
|
|
const handleRemoveSupport = useCallback(
|
|
(index: number) => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
support: prev.keyLevels.support.filter((_, i) => i !== index),
|
|
},
|
|
}));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleRemoveResistance = useCallback(
|
|
(index: number) => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
resistance: prev.keyLevels.resistance.filter((_, i) => i !== index),
|
|
},
|
|
}));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleUpdateSupport = useCallback(
|
|
(index: number, value: number) => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
support: prev.keyLevels.support.map((level, i) => (i === index ? value : level)),
|
|
},
|
|
}));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
const handleUpdateResistance = useCallback(
|
|
(index: number, value: number) => {
|
|
setPlan((prev) => ({
|
|
...prev,
|
|
keyLevels: {
|
|
...prev.keyLevels,
|
|
resistance: prev.keyLevels.resistance.map((level, i) => (i === index ? value : level)),
|
|
},
|
|
}));
|
|
},
|
|
[setPlan]
|
|
);
|
|
|
|
return (
|
|
<div className="space-y-6 rounded-2xl border border-slate-700 bg-slate-900 p-6">
|
|
<PlanHeader
|
|
planDate={plan.date}
|
|
isEditing={isEditing}
|
|
generating={generating}
|
|
onEdit={handleEdit}
|
|
onSave={handleSave}
|
|
onGenerateAI={handleGenerateAI}
|
|
onReset={() => setShowResetConfirm(true)}
|
|
/>
|
|
|
|
{/* Strategy Mode Info Banner */}
|
|
<div className="rounded-lg border border-blue-500/30 bg-blue-500/10 px-4 py-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-2xl">{STRATEGY_PRESETS[plan.strategyMode].emoji}</span>
|
|
<div>
|
|
<p className="font-semibold text-blue-300">
|
|
{plan.strategyMode} Mode Active
|
|
</p>
|
|
<p className="text-xs text-blue-200">
|
|
Max {STRATEGY_PRESETS[plan.strategyMode].maxDailyTrades} trades •
|
|
R:R 1:{STRATEGY_PRESETS[plan.strategyMode].r2rRatio.toFixed(1)} •
|
|
Stop: {STRATEGY_PRESETS[plan.strategyMode].stopLossPercent}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{(error || (openRouterReady === false && openRouterMessage)) && (
|
|
<div
|
|
className={`rounded-lg border px-4 py-3 ${
|
|
openRouterReady === false
|
|
? 'border-amber-500/60 bg-amber-500/10 text-amber-100'
|
|
: 'border-red-500/60 bg-red-500/10 text-red-100'
|
|
}`}
|
|
role="alert"
|
|
>
|
|
<p className="text-sm">{error || openRouterMessage}</p>
|
|
</div>
|
|
)}
|
|
|
|
<PlanBiasSelector bias={plan.bias} isEditing={isEditing} onChange={handleBiasChange} />
|
|
|
|
<div className="hidden sm:block">
|
|
<StrategyModeSelector
|
|
defaultMode={plan.strategyMode}
|
|
onModeChange={handleStrategyModeChange}
|
|
variant="compact"
|
|
/>
|
|
</div>
|
|
|
|
<div className="sm:hidden">
|
|
<StrategyModeSelector
|
|
defaultMode={plan.strategyMode}
|
|
onModeChange={handleStrategyModeChange}
|
|
variant="full"
|
|
/>
|
|
</div>
|
|
|
|
<PlanRiskParameters
|
|
dailyTarget={plan.dailyTarget}
|
|
maxLoss={plan.maxLoss}
|
|
maxTrades={plan.maxTrades}
|
|
entryZoneMin={plan.entryZone.min}
|
|
entryZoneMax={plan.entryZone.max}
|
|
targetPrice={plan.targetPrice}
|
|
stopLoss={plan.stopLoss}
|
|
isEditing={isEditing}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
|
|
<PlanKeyLevelsEditor
|
|
support={plan.keyLevels.support}
|
|
resistance={plan.keyLevels.resistance}
|
|
isEditing={isEditing}
|
|
onAddSupport={handleAddSupport}
|
|
onAddResistance={handleAddResistance}
|
|
onRemoveSupport={handleRemoveSupport}
|
|
onRemoveResistance={handleRemoveResistance}
|
|
onUpdateSupport={handleUpdateSupport}
|
|
onUpdateResistance={handleUpdateResistance}
|
|
/>
|
|
|
|
{/* Phase 3: Swing Trading Features - Show for SWING and HYBRID modes */}
|
|
{(plan.strategyMode === 'SWING' || plan.strategyMode === 'HYBRID') && (
|
|
<div className="space-y-6">
|
|
{/* Trend Confirmation for Swing Entry */}
|
|
<TrendConfirmation
|
|
ema8={2035.50}
|
|
ema21={2033.20}
|
|
ema55={2031.80}
|
|
ema200={2030.00}
|
|
macdLine={0.45}
|
|
macdSignal={0.32}
|
|
rsi={58.5}
|
|
timeframe={plan.strategyMode === 'SWING' ? '4h' : '1h'}
|
|
onTrendUpdate={(trend) => {
|
|
setPlan(prev => ({ ...prev, trendConfirmed: trend.strength !== 'WEAK' }));
|
|
}}
|
|
/>
|
|
|
|
{/* Multi-Day Position Tracker */}
|
|
{plan.swingPositions && plan.swingPositions.length > 0 && (
|
|
<MultiDayPositionTracker
|
|
positions={plan.swingPositions}
|
|
onMetricsUpdate={(metrics) => {
|
|
console.log('Position metrics updated:', metrics);
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* News Event Monitor */}
|
|
{plan.newsEvents && plan.newsEvents.length > 0 && (
|
|
<NewsEventTracker
|
|
events={plan.newsEvents}
|
|
onEventAlert={(event) => {
|
|
console.log('News event alert:', event.title);
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Phase 4 Advanced Metrics Snapshot */}
|
|
<div className="space-y-4 rounded-2xl border border-emerald-500/30 bg-emerald-500/5 p-4">
|
|
<div className="flex flex-wrap items-baseline justify-between gap-3">
|
|
<div>
|
|
<p className="text-xs uppercase tracking-[0.3em] text-emerald-300">Phase 4 · Execution intel</p>
|
|
<p className="text-lg font-semibold text-white">Advanced Metrics Summary</p>
|
|
<p className="text-xs text-emerald-200">
|
|
{advancedTradesSource === 'live'
|
|
? 'Powered by your closed trades.'
|
|
: 'Showing curated sample data until you close a trade.'}
|
|
</p>
|
|
</div>
|
|
<div className="text-right text-sm text-emerald-200">
|
|
<p>{advancedAnalyticsSummary ? `${advancedAnalyticsSummary.totalTrades} trades` : 'No trades yet'}</p>
|
|
<p className="font-semibold text-emerald-300">
|
|
{advancedAnalyticsSummary ? `${advancedAnalyticsSummary.winRate.toFixed(1)}% win rate` : 'Run a session to unlock insights'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{advancedAnalyticsSummary ? (
|
|
<>
|
|
<div className="grid gap-3 md:grid-cols-3">
|
|
<div className="rounded-xl border border-emerald-400/30 bg-emerald-400/5 p-3">
|
|
<p className="text-xs text-emerald-200/80">Best timeframe</p>
|
|
<p className="text-lg font-semibold text-white">
|
|
{advancedAnalyticsSummary.bestTimeframe ?? '—'}
|
|
</p>
|
|
<p className="text-xs text-emerald-200/70">Highest net P&L</p>
|
|
</div>
|
|
|
|
<div className="rounded-xl border border-emerald-400/30 bg-emerald-400/5 p-3">
|
|
<p className="text-xs text-emerald-200/80">Best signal</p>
|
|
<p className="text-lg font-semibold text-white">
|
|
{advancedAnalyticsSummary.bestSignal
|
|
? SIGNAL_LABELS[advancedAnalyticsSummary.bestSignal]
|
|
: '—'}
|
|
</p>
|
|
<p className="text-xs text-emerald-200/70">Highest risk / reward</p>
|
|
</div>
|
|
|
|
<div className="rounded-xl border border-emerald-400/30 bg-emerald-400/5 p-3">
|
|
<p className="text-xs text-emerald-200/80">Avg slippage impact</p>
|
|
<p className="text-lg font-semibold text-white">
|
|
{Number.isFinite(advancedAnalyticsSummary.slippageImpact)
|
|
? `${advancedAnalyticsSummary.slippageImpact.toFixed(1)}%`
|
|
: '—'}
|
|
</p>
|
|
<p className="text-xs text-emerald-200/70">Cost of fills vs. gross</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<div className="flex flex-wrap gap-2 text-xs text-emerald-200">
|
|
{analyticsFocus.timeframe && (
|
|
<span className="rounded-full border border-emerald-400/60 bg-emerald-400/10 px-3 py-1">
|
|
Timeframe: {analyticsFocus.timeframe}
|
|
</span>
|
|
)}
|
|
{analyticsFocus.signalType && (
|
|
<span className="rounded-full border border-emerald-400/60 bg-emerald-400/10 px-3 py-1">
|
|
Signal: {SIGNAL_LABELS[analyticsFocus.signalType]}
|
|
</span>
|
|
)}
|
|
{analyticsFocus.volatility && (
|
|
<span className="rounded-full border border-emerald-400/60 bg-emerald-400/10 px-3 py-1">
|
|
Volatility: {analyticsFocus.volatility}
|
|
</span>
|
|
)}
|
|
{!analyticsFocus.timeframe && !analyticsFocus.signalType && !analyticsFocus.volatility && (
|
|
<span className="text-emerald-200/70">No filter focus selected</span>
|
|
)}
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowAdvancedMetrics((prev) => !prev)}
|
|
className="inline-flex items-center gap-2 rounded-full border border-emerald-400/60 px-4 py-1 text-sm font-semibold text-emerald-100 transition hover:bg-emerald-400/10"
|
|
>
|
|
{showAdvancedMetrics ? 'Hide dashboard' : 'Open dashboard'}
|
|
</button>
|
|
</div>
|
|
|
|
{showAdvancedMetrics && (
|
|
<div className="rounded-2xl border border-slate-700 bg-slate-950/40 p-2">
|
|
<AdvancedMetricsDashboard
|
|
trades={safeAdvancedTrades}
|
|
onTimeframeSelect={handleAdvancedTimeframeSelect}
|
|
onSignalTypeSelect={handleAdvancedSignalSelect}
|
|
onVolatilityRangeSelect={handleAdvancedVolatilitySelect}
|
|
/>
|
|
</div>
|
|
)}
|
|
</>
|
|
) : (
|
|
<div className="rounded-xl border border-dashed border-emerald-400/40 bg-slate-900/40 p-4 text-sm text-emerald-200">
|
|
Close at least one trade to unlock the Advanced Metrics Dashboard summary.
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Trading Notes */}
|
|
<div className="space-y-2">
|
|
<label htmlFor="trading-notes" className="text-sm font-medium text-slate-400">
|
|
Trading Notes
|
|
</label>
|
|
{isEditing ? (
|
|
<textarea
|
|
id="trading-notes"
|
|
value={plan.tradingNotes}
|
|
onChange={(e) => handleNotesChange(e.target.value)}
|
|
rows={4}
|
|
className="w-full rounded-lg border border-slate-600 bg-slate-800 px-3 py-2 text-white focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/50"
|
|
placeholder="Enter your trading notes, observations, and strategies..."
|
|
/>
|
|
) : (
|
|
<div className="rounded-lg border border-slate-700 bg-slate-800/50 px-4 py-3 text-slate-300">
|
|
{plan.tradingNotes || <span className="italic text-slate-500">No notes</span>}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Modals */}
|
|
<ConfirmModal
|
|
isOpen={showResetConfirm}
|
|
onClose={() => setShowResetConfirm(false)}
|
|
onConfirm={handleConfirmReset}
|
|
title="Reset Plan"
|
|
message="Create a new plan for today? This will clear the current plan."
|
|
variant="warning"
|
|
/>
|
|
|
|
<AlertModal
|
|
isOpen={showSuccessAlert}
|
|
onClose={() => setShowSuccessAlert(false)}
|
|
title="Success"
|
|
message={successMessage}
|
|
variant="success"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Re-export types
|
|
export type { TradingPlan, DailyTradingPlanProps };
|