import { Component, signal, OnInit, OnDestroy, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSnackBarModule, MatSnackBar } from '@angular/material/snack-bar'; import { ApiService } from '../../services/api.service'; import { WebSocketService } from '../../services/websocket.service'; import { interval, Subscription } from 'rxjs'; export interface ExtendedMarketData { symbol: string; price: number; change: number; changePercent: number; volume: number; marketCap: string; high52Week: number; low52Week: number; } @Component({ selector: 'app-market-data', standalone: true, imports: [ CommonModule, MatCardModule, MatButtonModule, MatIconModule, MatTableModule, MatTabsModule, MatProgressSpinnerModule, MatSnackBarModule ], templateUrl: './market-data.component.html', styleUrl: './market-data.component.css' }) export class MarketDataComponent implements OnInit, OnDestroy { private apiService = inject(ApiService); private webSocketService = inject(WebSocketService); private snackBar = inject(MatSnackBar); private subscriptions: Subscription[] = []; protected marketData = signal([]); protected currentTime = signal(new Date().toLocaleTimeString()); protected isLoading = signal(true); protected error = signal(null); protected displayedColumns: string[] = ['symbol', 'price', 'change', 'changePercent', 'volume', 'marketCap']; ngOnInit() { // Update time every second const timeSubscription = interval(1000).subscribe(() => { this.currentTime.set(new Date().toLocaleTimeString()); }); this.subscriptions.push(timeSubscription); // Load initial market data this.loadMarketData(); // Subscribe to real-time market data updates const wsSubscription = this.webSocketService.getMarketDataUpdates().subscribe({ next: (update) => { this.updateMarketData(update); }, error: (err) => { console.error('WebSocket market data error:', err); } }); this.subscriptions.push(wsSubscription); // Fallback: Refresh market data every 30 seconds if WebSocket fails const dataSubscription = interval(30000).subscribe(() => { if (!this.webSocketService.isConnected()) { this.loadMarketData(); } }); this.subscriptions.push(dataSubscription); } ngOnDestroy() { this.subscriptions.forEach(sub => sub.unsubscribe()); } private loadMarketData() { this.apiService.getMarketData().subscribe({ next: (response) => { // Convert MarketData to ExtendedMarketData with mock extended properties const extendedData: ExtendedMarketData[] = response.data.map(item => ({ ...item, marketCap: this.getMockMarketCap(item.symbol), high52Week: item.price * 1.3, // Mock 52-week high (30% above current) low52Week: item.price * 0.7 // Mock 52-week low (30% below current) })); this.marketData.set(extendedData); this.isLoading.set(false); this.error.set(null); }, error: (err) => { console.error('Failed to load market data:', err); this.error.set('Failed to load market data'); this.isLoading.set(false); this.snackBar.open('Failed to load market data', 'Dismiss', { duration: 5000 }); // Use mock data as fallback this.marketData.set(this.getMockData()); } }); } private getMockMarketCap(symbol: string): string { const marketCaps: { [key: string]: string } = { 'AAPL': '2.98T', 'GOOGL': '1.78T', 'MSFT': '3.08T', 'TSLA': '789.2B', 'AMZN': '1.59T' }; return marketCaps[symbol] || '1.00T'; } private getMockData(): ExtendedMarketData[] { return [ { symbol: 'AAPL', price: 192.53, change: 2.41, changePercent: 1.27, volume: 45230000, marketCap: '2.98T', high52Week: 199.62, low52Week: 164.08 }, { symbol: 'GOOGL', price: 2847.56, change: -12.34, changePercent: -0.43, volume: 12450000, marketCap: '1.78T', high52Week: 3030.93, low52Week: 2193.62 }, { symbol: 'MSFT', price: 415.26, change: 8.73, changePercent: 2.15, volume: 23180000, marketCap: '3.08T', high52Week: 468.35, low52Week: 309.45 }, { symbol: 'TSLA', price: 248.50, change: -5.21, changePercent: -2.05, volume: 89760000, marketCap: '789.2B', high52Week: 299.29, low52Week: 152.37 }, { symbol: 'AMZN', price: 152.74, change: 3.18, changePercent: 2.12, volume: 34520000, marketCap: '1.59T', high52Week: 170.17, low52Week: 118.35 } ]; } refreshData() { this.isLoading.set(true); this.loadMarketData(); } private updateMarketData(update: any) { const currentData = this.marketData(); const updatedData = currentData.map(item => { if (item.symbol === update.symbol) { return { ...item, price: update.price, change: update.change, changePercent: update.changePercent, volume: update.volume }; } return item; }); this.marketData.set(updatedData); } }