Initial commit: Gold Trading Simulator with AI-powered analysis

This commit is contained in:
Krikorios
2025-11-16 00:50:04 +02:00
commit 72c1d3adb7
128 changed files with 16232 additions and 0 deletions
+296
View File
@@ -0,0 +1,296 @@
import { useState, ReactNode } from 'react';
import { X, Maximize2, Minimize2, Pin, PinOff } from 'lucide-react';
import ComponentSettings from './ComponentSettings';
import type { TabConfig, LayoutMode, TabCustomization } from '@/types';
interface TabPanel {
config: TabConfig;
content: ReactNode;
}
interface TabbedContainerProps {
panels: TabPanel[];
mode: LayoutMode;
onTabClose?: (tabId: string) => void;
onTabPin?: (tabId: string) => void;
onCustomizationUpdate?: (tabId: string, customization: TabCustomization) => void;
className?: string;
}
export default function TabbedContainer({
panels,
mode,
onTabClose,
onTabPin,
onCustomizationUpdate,
className = '',
}: TabbedContainerProps) {
const [activeTabId, setActiveTabId] = useState<string>(
panels.find((p) => p.config.visible)?.config.id || panels[0]?.config.id
);
const [expandedTab, setExpandedTab] = useState<string | null>(null);
const visiblePanels = panels
.filter((p) => p.config.visible)
.sort((a, b) => a.config.order - b.config.order);
const activePanel = visiblePanels.find((p) => p.config.id === activeTabId);
const handleExpand = (tabId: string) => {
setExpandedTab(expandedTab === tabId ? null : tabId);
};
// Tabs mode - single panel with tab switcher
if (mode === 'tabs') {
return (
<div className={`flex flex-col h-full ${className}`}>
{/* Tab switcher */}
<div className="flex items-center gap-1 border-b border-gray-700 bg-dark-card px-2 overflow-x-auto">
{visiblePanels.map((panel) => (
<button
key={panel.config.id}
onClick={() => setActiveTabId(panel.config.id)}
className={`flex items-center gap-2 px-4 py-3 border-b-2 transition-colors whitespace-nowrap ${
activeTabId === panel.config.id
? 'border-gold-500 text-gold-500 bg-dark-hover'
: 'border-transparent text-gray-400 hover:text-white hover:bg-dark-hover'
}`}
>
{panel.config.pinned && <Pin className="w-3 h-3" />}
<span>{panel.config.label}</span>
{onTabClose && !panel.config.pinned && (
<button
onClick={(e) => {
e.stopPropagation();
onTabClose(panel.config.id);
}}
className="p-0.5 hover:bg-red-500/20 rounded"
>
<X className="w-3 h-3" />
</button>
)}
</button>
))}
</div>
{/* Active panel content */}
<div className="flex-1 overflow-hidden">
{activePanel && (
<div className="h-full">{activePanel.content}</div>
)}
</div>
</div>
);
}
// Split mode - two main sections
if (mode === 'split') {
const leftPanels = visiblePanels.filter((p) => p.config.position !== 'right');
const rightPanels = visiblePanels.filter((p) => p.config.position === 'right');
return (
<div className={`grid grid-cols-2 gap-6 h-full ${className}`}>
{/* Left section */}
<div className="space-y-6 overflow-y-auto">
{leftPanels.map((panel) => (
<PanelCard
key={panel.config.id}
panel={panel}
onExpand={handleExpand}
onPin={onTabPin}
onClose={onTabClose}
onCustomizationUpdate={onCustomizationUpdate}
isExpanded={expandedTab === panel.config.id}
/>
))}
</div>
{/* Right section */}
<div className="space-y-6 overflow-y-auto">
{rightPanels.map((panel) => (
<PanelCard
key={panel.config.id}
panel={panel}
onExpand={handleExpand}
onPin={onTabPin}
onClose={onTabClose}
onCustomizationUpdate={onCustomizationUpdate}
isExpanded={expandedTab === panel.config.id}
/>
))}
</div>
</div>
);
}
// Grid mode - responsive grid layout
const getSizeClass = (size: TabConfig['size']) => {
switch (size) {
case 'small':
return 'col-span-1';
case 'medium':
return 'col-span-1 lg:col-span-2';
case 'large':
return 'col-span-1 lg:col-span-3';
case 'full':
return 'col-span-full';
default:
return 'col-span-1';
}
};
return (
<div className={`grid grid-cols-1 lg:grid-cols-6 gap-6 ${className}`}>
{visiblePanels.map((panel) => (
<div
key={panel.config.id}
className={`${getSizeClass(panel.config.size)} ${
expandedTab === panel.config.id ? 'fixed inset-4 z-40' : ''
}`}
>
<PanelCard
panel={panel}
onExpand={handleExpand}
onPin={onTabPin}
onClose={onTabClose}
onCustomizationUpdate={onCustomizationUpdate}
isExpanded={expandedTab === panel.config.id}
/>
</div>
))}
</div>
);
}
// Individual panel card component
interface PanelCardProps {
panel: TabPanel;
onExpand: (tabId: string) => void;
onPin?: (tabId: string) => void;
onClose?: (tabId: string) => void;
onCustomizationUpdate?: (tabId: string, customization: TabCustomization) => void;
isExpanded: boolean;
}
function PanelCard({ panel, onExpand, onPin, onClose, onCustomizationUpdate, isExpanded }: PanelCardProps) {
const getAvailableSettings = (tabId: string) => {
// Define available settings per tab
switch (tabId) {
case 'news':
return {
autoRefresh: true,
refreshRate: true,
filters: {
sentiment: ['ALL', 'POSITIVE', 'NEGATIVE', 'NEUTRAL'],
impact: ['ALL', 'HIGH', 'MEDIUM', 'LOW'],
},
theme: true,
};
case 'alerts':
return {
autoRefresh: true,
refreshRate: true,
filters: {
severity: ['ALL', 'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'],
type: ['ALL', 'PRICE_SPIKE', 'NEWS_BREAKING', 'SUPPORT_BREACH'],
},
};
case 'chart':
return {
displayMode: ['candlestick', 'line', 'area'],
theme: true,
};
case 'analytics':
return {
displayMode: ['detailed', 'compact', 'charts-only'],
theme: true,
};
case 'daily-checklist':
return {
autoRefresh: false,
displayMode: ['all-phases', 'current-phase-only'],
theme: true,
};
case 'trading-journal':
return {
displayMode: ['detailed', 'compact', 'list'],
filters: {
emotion: ['all', 'confident', 'neutral', 'anxious'],
result: ['all', 'winners', 'losers'],
},
};
case 'market-summary':
return {
autoRefresh: true,
refreshRate: true,
displayMode: ['overview', 'levels', 'events', 'ai'],
};
default:
return { theme: true };
}
};
return (
<div
className={`card flex flex-col group ${
isExpanded ? 'h-full' : 'h-auto'
}`}
>
{/* Panel header */}
<div className="flex items-center justify-between mb-4 pb-3 border-b border-gray-700">
<h3 className="text-lg font-semibold flex items-center gap-2">
{panel.config.pinned && <Pin className="w-4 h-4 text-gold-500" />}
{panel.config.label}
</h3>
<div className="flex items-center gap-1">
{onCustomizationUpdate && (
<ComponentSettings
customization={panel.config.customization}
onUpdate={(customization) =>
onCustomizationUpdate(panel.config.id, customization)
}
availableSettings={getAvailableSettings(panel.config.id)}
/>
)}
{onPin && (
<button
onClick={() => onPin(panel.config.id)}
className="p-1.5 hover:bg-dark-hover rounded transition-colors"
title={panel.config.pinned ? 'Unpin' : 'Pin'}
>
{panel.config.pinned ? (
<PinOff className="w-4 h-4" />
) : (
<Pin className="w-4 h-4" />
)}
</button>
)}
<button
onClick={() => onExpand(panel.config.id)}
className="p-1.5 hover:bg-dark-hover rounded transition-colors"
title={isExpanded ? 'Minimize' : 'Expand'}
>
{isExpanded ? (
<Minimize2 className="w-4 h-4" />
) : (
<Maximize2 className="w-4 h-4" />
)}
</button>
{onClose && !panel.config.pinned && (
<button
onClick={() => onClose(panel.config.id)}
className="p-1.5 hover:bg-red-500/20 rounded transition-colors"
title="Close"
>
<X className="w-4 h-4" />
</button>
)}
</div>
</div>
{/* Panel content */}
<div className={`flex-1 ${isExpanded ? 'overflow-y-auto' : ''}`}>
{panel.content}
</div>
</div>
);
}