diff --git a/client/src/pages/os.tsx b/client/src/pages/os.tsx index 9853414..51c1fec 100644 --- a/client/src/pages/os.tsx +++ b/client/src/pages/os.tsx @@ -8,7 +8,7 @@ import { Terminal, FileText, IdCard, Music, Settings, Globe, X, Minus, Square, Maximize2, Volume2, Wifi, Battery, ChevronUp, FolderOpen, Award, MessageCircle, Send, - ExternalLink, User, LogOut, BarChart3, Loader2, + ExternalLink, User, LogOut, BarChart3, Loader2, Layers, Presentation, Bell, Image, Monitor, Play, Pause, ChevronRight, Network, Activity, Code2, Radio, Newspaper, Gamepad2, Users, Trophy, Calculator, StickyNote, Cpu, Camera, @@ -1075,11 +1075,119 @@ export default function AeThexOS() { ); } +interface WidgetPosition { + x: number; + y: number; +} + +interface WidgetPositions { + [key: string]: WidgetPosition; +} + +function getDefaultWidgetPositions(): WidgetPositions { + const w = typeof window !== 'undefined' ? window.innerWidth : 1200; + const h = typeof window !== 'undefined' ? window.innerHeight : 800; + return { + clock: { x: w - 220, y: 16 }, + weather: { x: w - 220, y: 100 }, + status: { x: w - 220, y: 200 }, + notifications: { x: w - 220, y: 320 }, + leaderboard: { x: w - 440, y: 16 }, + pipeline: { x: w - 440, y: 180 }, + kpi: { x: w - 440, y: 340 }, + heartbeat: { x: 16, y: h - 180 }, + }; +} + +function DraggableWidget({ + id, + children, + positions, + onPositionChange, + className = "" +}: { + id: string; + children: React.ReactNode; + positions: WidgetPositions; + onPositionChange: (id: string, pos: WidgetPosition) => void; + className?: string; +}) { + const [isDragging, setIsDragging] = useState(false); + const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); + const widgetRef = useRef(null); + + const defaultPositions = getDefaultWidgetPositions(); + const position = positions[id] || defaultPositions[id] || { x: 100, y: 100 }; + + const handleMouseDown = (e: React.MouseEvent) => { + if ((e.target as HTMLElement).closest('.widget-drag-handle')) { + e.preventDefault(); + setIsDragging(true); + setDragOffset({ + x: e.clientX - position.x, + y: e.clientY - position.y + }); + } + }; + + useEffect(() => { + if (!isDragging) return; + + const handleMouseMove = (e: MouseEvent) => { + const newX = Math.max(0, Math.min(window.innerWidth - 50, e.clientX - dragOffset.x)); + const newY = Math.max(0, Math.min(window.innerHeight - 60, e.clientY - dragOffset.y)); + onPositionChange(id, { x: newX, y: newY }); + }; + + const handleMouseUp = () => { + setIsDragging(false); + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + return () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + }; + }, [isDragging, dragOffset, id, onPositionChange]); + + return ( + +
+
+
+
+
+
+
+ {children} + + ); +} + function DesktopWidgets({ time, weather, notifications }: { time: Date; weather?: { current_weather?: { temperature: number; windspeed: number; weathercode: number } }; notifications?: string[]; }) { + const [widgetPositions, setWidgetPositions] = useState(() => { + const saved = localStorage.getItem('aethex-widget-positions'); + return saved ? JSON.parse(saved) : getDefaultWidgetPositions(); + }); + const { data: metrics } = useQuery({ queryKey: ['os-metrics'], queryFn: async () => { @@ -1089,6 +1197,24 @@ function DesktopWidgets({ time, weather, notifications }: { refetchInterval: 30000, }); + const { data: leaderboard } = useQuery({ + queryKey: ['os-leaderboard'], + queryFn: async () => { + const res = await fetch('/api/directory/architects'); + const data = await res.json(); + return data.slice(0, 5); + }, + refetchInterval: 60000, + }); + + const handlePositionChange = useCallback((id: string, pos: WidgetPosition) => { + setWidgetPositions(prev => { + const updated = { ...prev, [id]: pos }; + localStorage.setItem('aethex-widget-positions', JSON.stringify(updated)); + return updated; + }); + }, []); + const getWeatherIcon = (code: number) => { if (code === 0) return '☀️'; if (code <= 3) return '⛅'; @@ -1100,79 +1226,208 @@ function DesktopWidgets({ time, weather, notifications }: { return '⛈️'; }; + const getNotificationCategory = (text: string) => { + if (text.toLowerCase().includes('security') || text.toLowerCase().includes('aegis')) + return { color: 'text-green-400', icon: }; + if (text.toLowerCase().includes('project')) + return { color: 'text-purple-400', icon: }; + return { color: 'text-cyan-400', icon: }; + }; + return ( -
- -
- {time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+ +
+
+ {time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+
+ {time.toLocaleDateString([], { weekday: 'long', month: 'short', day: 'numeric' })} +
-
- {time.toLocaleDateString([], { weekday: 'long', month: 'short', day: 'numeric' })} -
- +
{weather?.current_weather && ( - -
Weather
-
- {getWeatherIcon(weather.current_weather.weathercode)} -
-
{Math.round(weather.current_weather.temperature)}°F
-
Wind: {weather.current_weather.windspeed} mph
+ +
+
Weather
+
+ {getWeatherIcon(weather.current_weather.weathercode)} +
+
{Math.round(weather.current_weather.temperature)}°F
+
Wind: {weather.current_weather.windspeed} mph
+
- +
)} {metrics && ( - -
System Status
-
-
- Architects - {metrics.totalProfiles || 0} -
-
- Projects - {metrics.totalProjects || 0} -
-
- Online - {metrics.onlineUsers || 0} + +
+
System Status
+
+
+ Architects +
+ {metrics.totalProfiles || 0} + +
+
+
+ Projects +
+ {metrics.totalProjects || 0} + +
+
+
+ Online + {metrics.onlineUsers || 0} +
+
+ Verified + {metrics.verifiedUsers || 0} +
- +
)} {notifications && notifications.length > 0 && ( - -
Notifications
-
- {notifications.slice(0, 3).map((n, i) => ( -
{n}
- ))} + +
+
Notifications
+
+ {notifications.slice(0, 4).map((n, i) => { + const cat = getNotificationCategory(n); + return ( +
+ {cat.icon} + {n} +
+ ); + })} +
- +
)} + + {leaderboard && leaderboard.length > 0 && ( + +
+
+ + Top Architects +
+
+ {leaderboard.map((arch: any, i: number) => ( +
+ + {i + 1} + + {arch.username || arch.display_name} + Lv{arch.level || 1} +
+ ))} +
+
+
+ )} + + {metrics && ( + +
+
+ + Project Pipeline +
+
+
+
+ Active + {metrics.totalProjects || 0} +
+
+
+
+
+
+
+ In Review + 2 +
+
+
+
+
+
+
+ Completed + 12 +
+
+
+
+
+
+
+ + )} + + {metrics && ( + +
+
+ + Key Metrics +
+
+
+
{metrics.totalXP || 0}
+
Total XP
+
+
+
{metrics.avgLevel || 1}
+
Avg Level
+
+
+
{metrics.verifiedUsers || 0}
+
Verified
+
+
+
98%
+
Uptime
+
+
+
+
+ )} + + +
+
+ + Network Pulse +
+
+ + + +
+
+ All Systems Operational +
+
+
); }