Created
June 18, 2025 05:54
-
-
Save xbalajipge/415c92b6e3d7535ac6e78bf11ded6b9b to your computer and use it in GitHub Desktop.
html-universal-stock-dashboard.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Universal Stock Dashboard</title> | |
| <style> | |
| /* Generated by Copilot */ | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: rgba(255, 255, 255, 0.95); | |
| border-radius: 20px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); | |
| overflow: hidden; | |
| backdrop-filter: blur(10px); | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #2c3e50, #34495e); | |
| color: white; | |
| padding: 30px; | |
| text-align: center; | |
| } | |
| .header h1 { | |
| font-size: 2.5rem; | |
| margin-bottom: 10px; | |
| font-weight: 700; | |
| } | |
| .header p { | |
| font-size: 1.1rem; | |
| opacity: 0.9; | |
| } | |
| .search-section { | |
| background: linear-gradient(135deg, #34495e, #2c3e50); | |
| padding: 30px; | |
| color: white; | |
| } | |
| .search-container { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| align-items: center; | |
| } | |
| .search-input { | |
| flex: 1; | |
| min-width: 200px; | |
| padding: 15px 20px; | |
| border: none; | |
| border-radius: 25px; | |
| font-size: 1.1rem; | |
| background: rgba(255, 255, 255, 0.1); | |
| color: white; | |
| backdrop-filter: blur(10px); | |
| transition: all 0.3s ease; | |
| } | |
| .search-input::placeholder { | |
| color: rgba(255, 255, 255, 0.7); | |
| } | |
| .search-input:focus { | |
| outline: none; | |
| background: rgba(255, 255, 255, 0.2); | |
| box-shadow: 0 0 20px rgba(255, 255, 255, 0.1); | |
| } | |
| .search-btn { | |
| background: linear-gradient(135deg, #3498db, #2980b9); | |
| color: white; | |
| border: none; | |
| padding: 15px 30px; | |
| border-radius: 25px; | |
| cursor: pointer; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| min-width: 120px; | |
| } | |
| .search-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4); | |
| } | |
| .search-btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .historic-section { | |
| display: flex; | |
| gap: 15px; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| } | |
| .date-input { | |
| padding: 12px 15px; | |
| border: none; | |
| border-radius: 20px; | |
| background: rgba(255, 255, 255, 0.1); | |
| color: white; | |
| backdrop-filter: blur(10px); | |
| font-size: 1rem; | |
| } | |
| .date-input::-webkit-calendar-picker-indicator { | |
| filter: invert(1); | |
| } | |
| .historic-btn { | |
| background: linear-gradient(135deg, #e74c3c, #c0392b); | |
| color: white; | |
| border: none; | |
| padding: 12px 20px; | |
| border-radius: 20px; | |
| cursor: pointer; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| } | |
| .historic-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(231, 76, 60, 0.4); | |
| } | |
| .company-info { | |
| background: linear-gradient(135deg, #1e3c72, #2a5298); | |
| color: white; | |
| padding: 30px; | |
| text-align: center; | |
| display: none; | |
| } | |
| .company-name { | |
| font-size: 2.2rem; | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| } | |
| .company-ticker { | |
| font-size: 1.2rem; | |
| opacity: 0.8; | |
| margin-bottom: 15px; | |
| } | |
| .stock-price { | |
| background: linear-gradient(135deg, #1e3c72, #2a5298); | |
| color: white; | |
| padding: 30px; | |
| text-align: center; | |
| display: none; | |
| } | |
| .price-display { | |
| font-size: 3rem; | |
| font-weight: bold; | |
| margin-bottom: 10px; | |
| } | |
| .price-change { | |
| font-size: 1.2rem; | |
| padding: 8px 16px; | |
| border-radius: 20px; | |
| display: inline-block; | |
| margin-top: 10px; | |
| } | |
| .price-change.positive { | |
| background: #27ae60; | |
| } | |
| .price-change.negative { | |
| background: #e74c3c; | |
| } | |
| .historic-price-display { | |
| background: linear-gradient(135deg, #8e44ad, #9b59b6); | |
| color: white; | |
| padding: 20px; | |
| margin-top: 20px; | |
| border-radius: 15px; | |
| text-align: center; | |
| display: none; | |
| } | |
| .historic-date { | |
| font-size: 1.1rem; | |
| margin-bottom: 10px; | |
| opacity: 0.9; | |
| } | |
| .historic-price { | |
| font-size: 2rem; | |
| font-weight: bold; | |
| } | |
| .dashboard-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 20px; | |
| padding: 30px; | |
| display: none; | |
| } | |
| .card { | |
| background: white; | |
| border-radius: 15px; | |
| padding: 25px; | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | |
| border-left: 4px solid #3498db; | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 15px 35px rgba(0, 0, 0, 0.15); | |
| } | |
| .card h3 { | |
| color: #2c3e50; | |
| margin-bottom: 15px; | |
| font-size: 1.3rem; | |
| display: flex; | |
| align-items: center; | |
| } | |
| .card-icon { | |
| margin-right: 10px; | |
| font-size: 1.5rem; | |
| } | |
| .metric { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 10px; | |
| padding: 8px 0; | |
| border-bottom: 1px solid #ecf0f1; | |
| } | |
| .metric:last-child { | |
| border-bottom: none; | |
| } | |
| .metric-label { | |
| color: #7f8c8d; | |
| font-weight: 500; | |
| } | |
| .metric-value { | |
| color: #2c3e50; | |
| font-weight: 600; | |
| } | |
| .chart-container { | |
| height: 300px; | |
| margin-top: 20px; | |
| background: #f8f9fa; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #7f8c8d; | |
| font-style: italic; | |
| } | |
| .news-item { | |
| padding: 15px; | |
| border-bottom: 1px solid #ecf0f1; | |
| cursor: pointer; | |
| transition: background-color 0.3s ease; | |
| } | |
| .news-item:hover { | |
| background-color: #f8f9fa; | |
| } | |
| .news-item:last-child { | |
| border-bottom: none; | |
| } | |
| .news-title { | |
| font-weight: 600; | |
| color: #2c3e50; | |
| margin-bottom: 5px; | |
| } | |
| .news-date { | |
| color: #7f8c8d; | |
| font-size: 0.9rem; | |
| } | |
| .loading { | |
| text-align: center; | |
| padding: 40px; | |
| color: #7f8c8d; | |
| font-size: 1.2rem; | |
| } | |
| .error { | |
| text-align: center; | |
| padding: 20px; | |
| color: #e74c3c; | |
| background: #fdf2f2; | |
| border-radius: 10px; | |
| margin: 20px; | |
| } | |
| .refresh-btn { | |
| background: linear-gradient(135deg, #3498db, #2980b9); | |
| color: white; | |
| border: none; | |
| padding: 12px 24px; | |
| border-radius: 25px; | |
| cursor: pointer; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| margin: 20px auto; | |
| display: none; | |
| } | |
| .refresh-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4); | |
| } | |
| .last-updated { | |
| text-align: center; | |
| color: #7f8c8d; | |
| font-size: 0.9rem; | |
| margin-top: 20px; | |
| padding: 20px; | |
| display: none; | |
| } | |
| .welcome-message { | |
| text-align: center; | |
| padding: 60px 40px; | |
| color: #7f8c8d; | |
| } | |
| .welcome-message h2 { | |
| font-size: 1.8rem; | |
| margin-bottom: 15px; | |
| color: #34495e; | |
| } | |
| .welcome-message p { | |
| font-size: 1.1rem; | |
| line-height: 1.6; | |
| } | |
| .popular-stocks { | |
| display: flex; | |
| gap: 10px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| margin-top: 20px; | |
| } | |
| .stock-chip { | |
| background: linear-gradient(135deg, #3498db, #2980b9); | |
| color: white; | |
| padding: 8px 16px; | |
| border-radius: 20px; | |
| cursor: pointer; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| border: none; | |
| } | |
| .stock-chip:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 3px 10px rgba(52, 152, 219, 0.3); | |
| } | |
| @media (max-width: 768px) { | |
| .dashboard-grid { | |
| grid-template-columns: 1fr; | |
| padding: 20px; | |
| } | |
| .header h1 { | |
| font-size: 2rem; | |
| } | |
| .price-display { | |
| font-size: 2.5rem; | |
| } | |
| .container { | |
| margin: 10px; | |
| border-radius: 15px; | |
| } | |
| .search-container { | |
| flex-direction: column; | |
| } | |
| .search-input { | |
| min-width: 100%; | |
| } | |
| .historic-section { | |
| justify-content: center; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| body { | |
| padding: 10px; | |
| } | |
| .header { | |
| padding: 20px; | |
| } | |
| .search-section { | |
| padding: 20px; | |
| } | |
| .stock-price { | |
| padding: 20px; | |
| } | |
| .dashboard-grid { | |
| padding: 15px; | |
| } | |
| .card { | |
| padding: 20px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>π Universal Stock Dashboard</h1> | |
| <p>Real-time stock information for any publicly traded company</p> | |
| </div> | |
| <div class="search-section"> | |
| <div class="search-container"> | |
| <input type="text" id="tickerInput" class="search-input" | |
| placeholder="Enter stock ticker (e.g., AAPL, TSLA, PCG)..." | |
| maxlength="10"> | |
| <button class="search-btn" onclick="searchStock()" id="searchBtn"> | |
| π Search Stock | |
| </button> | |
| </div> | |
| <div class="historic-section"> | |
| <label for="historicDate" style="margin-right: 10px;">Historic Price:</label> | |
| <input type="date" id="historicDate" class="date-input" max="2025-06-17"> | |
| <button class="historic-btn" onclick="getHistoricPrice()" id="historicBtn"> | |
| π Get Historic Price | |
| </button> | |
| </div> | |
| </div> | |
| <div id="welcomeSection" class="welcome-message"> | |
| <h2>Welcome to Universal Stock Dashboard</h2> | |
| <p>Enter any stock ticker symbol above to get comprehensive stock information including current price, earnings, dividends, fundamentals, and recent news.</p> | |
| <div class="popular-stocks"> | |
| <button class="stock-chip" onclick="quickSearch('AAPL')">AAPL</button> | |
| <button class="stock-chip" onclick="quickSearch('TSLA')">TSLA</button> | |
| <button class="stock-chip" onclick="quickSearch('MSFT')">MSFT</button> | |
| <button class="stock-chip" onclick="quickSearch('GOOGL')">GOOGL</button> | |
| <button class="stock-chip" onclick="quickSearch('AMZN')">AMZN</button> | |
| <button class="stock-chip" onclick="quickSearch('PCG')">PCG</button> | |
| <button class="stock-chip" onclick="quickSearch('META')">META</button> | |
| <button class="stock-chip" onclick="quickSearch('NVDA')">NVDA</button> | |
| </div> | |
| </div> | |
| <div class="company-info" id="companyInfo"> | |
| <div class="company-name" id="companyName"></div> | |
| <div class="company-ticker" id="companyTicker"></div> | |
| </div> | |
| <div class="stock-price" id="stockPrice"> | |
| <div class="price-display" id="currentPrice">Loading...</div> | |
| <div class="price-change" id="priceChange">--</div> | |
| <div class="historic-price-display" id="historicPriceDisplay"> | |
| <div class="historic-date" id="historicDateDisplay"></div> | |
| <div class="historic-price" id="historicPriceValue"></div> | |
| </div> | |
| </div> | |
| <button class="refresh-btn" onclick="refreshData()" id="refreshBtn" style="display: block;"> | |
| π Refresh Data | |
| </button> | |
| <div class="dashboard-grid" id="dashboardGrid"> | |
| <!-- Stock Trends Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π</span>Stock Trends</h3> | |
| <div class="metric"> | |
| <span class="metric-label">52-Week High</span> | |
| <span class="metric-value" id="weekHigh">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">52-Week Low</span> | |
| <span class="metric-value" id="weekLow">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Average Volume</span> | |
| <span class="metric-value" id="avgVolume">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Market Cap</span> | |
| <span class="metric-value" id="marketCap">--</span> | |
| </div> | |
| <div class="chart-container"> | |
| π Interactive chart would be displayed here with real API integration | |
| </div> | |
| </div> | |
| <!-- Earnings Report Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π°</span>Latest Earnings</h3> | |
| <div class="metric"> | |
| <span class="metric-label">Last EPS</span> | |
| <span class="metric-value" id="lastEPS">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Est. EPS</span> | |
| <span class="metric-value" id="estEPS">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Revenue</span> | |
| <span class="metric-value" id="revenue">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Next Earnings</span> | |
| <span class="metric-value" id="nextEarnings">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">P/E Ratio</span> | |
| <span class="metric-value" id="peRatio">--</span> | |
| </div> | |
| </div> | |
| <!-- Dividend Information Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π</span>Dividend Info</h3> | |
| <div class="metric"> | |
| <span class="metric-label">Annual Dividend</span> | |
| <span class="metric-value" id="annualDiv">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Dividend Yield</span> | |
| <span class="metric-value" id="divYield">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Ex-Dividend Date</span> | |
| <span class="metric-value" id="exDivDate">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Payment Date</span> | |
| <span class="metric-value" id="payDate">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Payout Ratio</span> | |
| <span class="metric-value" id="payoutRatio">--</span> | |
| </div> | |
| </div> | |
| <!-- Fundamentals Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π</span>Key Fundamentals</h3> | |
| <div class="metric"> | |
| <span class="metric-label">Book Value</span> | |
| <span class="metric-value" id="bookValue">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Price-to-Book</span> | |
| <span class="metric-value" id="priceToBook">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">ROE</span> | |
| <span class="metric-value" id="roe">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Debt-to-Equity</span> | |
| <span class="metric-value" id="debtToEquity">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Beta</span> | |
| <span class="metric-value" id="beta">--</span> | |
| </div> | |
| </div> | |
| <!-- Financial Health Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π₯</span>Financial Health</h3> | |
| <div class="metric"> | |
| <span class="metric-label">Current Ratio</span> | |
| <span class="metric-value" id="currentRatio">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Quick Ratio</span> | |
| <span class="metric-value" id="quickRatio">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Cash per Share</span> | |
| <span class="metric-value" id="cashPerShare">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Free Cash Flow</span> | |
| <span class="metric-value" id="freeCashFlow">--</span> | |
| </div> | |
| <div class="metric"> | |
| <span class="metric-label">Credit Rating</span> | |
| <span class="metric-value" id="creditRating">--</span> | |
| </div> | |
| </div> | |
| <!-- Recent News Card --> | |
| <div class="card"> | |
| <h3><span class="card-icon">π°</span>Recent News</h3> | |
| <div id="newsContainer"> | |
| <div class="loading">Loading news...</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="last-updated" id="lastUpdated"> | |
| Last updated: -- | |
| </div> | |
| </div> | |
| <script> | |
| // Generated by Copilot | |
| class UniversalStockDashboard { | |
| constructor() { | |
| this._currentTicker = null; | |
| this._currentPrice = null; | |
| this._priceChange = null; | |
| this._isLoading = false; | |
| this._lastUpdateTime = new Date(); | |
| this._companyData = new Map(); | |
| this._historicPrices = new Map(); | |
| this.initializeDashboard(); | |
| } | |
| initializeDashboard() { | |
| this.setupEventListeners(); | |
| this.setDefaultDate(); | |
| this.loadPopularStocks(); | |
| } | |
| setupEventListeners() { | |
| // Enter key support for ticker input | |
| document.getElementById('tickerInput').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| this.searchStock(); | |
| } | |
| }); | |
| // Enter key support for date input | |
| document.getElementById('historicDate').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| this.getHistoricPrice(); | |
| } | |
| }); | |
| // Auto-uppercase ticker input | |
| document.getElementById('tickerInput').addEventListener('input', (e) => { | |
| e.target.value = e.target.value.toUpperCase(); | |
| }); | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'r' || e.key === 'R') { | |
| e.preventDefault(); | |
| this.refreshData(); | |
| } | |
| if (e.key === '/' || e.key === 'f') { | |
| e.preventDefault(); | |
| document.getElementById('tickerInput').focus(); | |
| } | |
| }); | |
| } | |
| setDefaultDate() { | |
| const today = new Date(); | |
| const yesterday = new Date(today); | |
| yesterday.setDate(yesterday.getDate() - 1); | |
| document.getElementById('historicDate').value = yesterday.toISOString().split('T')[0]; | |
| } | |
| loadPopularStocks() { | |
| // Pre-load some company data for popular stocks | |
| this._companyData.set('AAPL', { name: 'Apple Inc.', sector: 'Technology' }); | |
| this._companyData.set('TSLA', { name: 'Tesla, Inc.', sector: 'Automotive' }); | |
| this._companyData.set('MSFT', { name: 'Microsoft Corporation', sector: 'Technology' }); | |
| this._companyData.set('GOOGL', { name: 'Alphabet Inc.', sector: 'Technology' }); | |
| this._companyData.set('AMZN', { name: 'Amazon.com Inc.', sector: 'E-commerce' }); | |
| this._companyData.set('PCG', { name: 'Pacific Gas and Electric Company', sector: 'Utilities' }); | |
| this._companyData.set('META', { name: 'Meta Platforms, Inc.', sector: 'Technology' }); | |
| this._companyData.set('NVDA', { name: 'NVIDIA Corporation', sector: 'Technology' }); | |
| } | |
| quickSearch(ticker) { | |
| document.getElementById('tickerInput').value = ticker; | |
| this.searchStock(); | |
| } | |
| async searchStock() { | |
| const ticker = document.getElementById('tickerInput').value.trim().toUpperCase(); | |
| if (!ticker) { | |
| this.showError('Please enter a stock ticker symbol'); | |
| return; | |
| } | |
| if (this._isLoading) return; | |
| this._isLoading = true; | |
| this._currentTicker = ticker; | |
| this.showLoading('Searching for ' + ticker + '...'); | |
| this.hideWelcomeSection(); | |
| try { | |
| // Simulate API call delay | |
| await this.delay(1000); | |
| // Generate mock data for the ticker | |
| await this.fetchStockData(ticker); | |
| this.showDashboard(); | |
| this.showNotification(`${ticker} data loaded successfully!`); | |
| } catch (error) { | |
| this.showError(`Failed to load data for ${ticker}. Please try again.`); | |
| } finally { | |
| this._isLoading = false; | |
| } | |
| } | |
| async getHistoricPrice() { | |
| if (!this._currentTicker) { | |
| this.showError('Please search for a stock first'); | |
| return; | |
| } | |
| const date = document.getElementById('historicDate').value; | |
| if (!date) { | |
| this.showError('Please select a date'); | |
| return; | |
| } | |
| const historicBtn = document.getElementById('historicBtn'); | |
| historicBtn.textContent = 'β³ Loading...'; | |
| historicBtn.disabled = true; | |
| try { | |
| await this.delay(800); | |
| const historicPrice = this.generateHistoricPrice(this._currentTicker, date); | |
| this.displayHistoricPrice(date, historicPrice); | |
| this.showNotification(`Historic price for ${date} loaded!`); | |
| } catch (error) { | |
| this.showError('Failed to load historic price'); | |
| } finally { | |
| historicBtn.textContent = 'π Get Historic Price'; | |
| historicBtn.disabled = false; | |
| } | |
| } | |
| generateHistoricPrice(ticker, date) { | |
| // Generate a realistic historic price based on current price and date | |
| const daysAgo = Math.floor((new Date() - new Date(date)) / (1000 * 60 * 60 * 24)); | |
| const volatility = Math.random() * 0.3 + 0.1; // 10-40% volatility | |
| const trend = (Math.random() - 0.5) * 2; // Random trend | |
| let basePrice = this._currentPrice || 100; | |
| const historicPrice = basePrice * (1 + (trend * volatility * daysAgo / 365)); | |
| return Math.max(historicPrice, 0.01); // Ensure positive price | |
| } | |
| displayHistoricPrice(date, price) { | |
| const historicDisplay = document.getElementById('historicPriceDisplay'); | |
| const dateDisplay = document.getElementById('historicDateDisplay'); | |
| const priceDisplay = document.getElementById('historicPriceValue'); | |
| dateDisplay.textContent = `Price on ${new Date(date).toLocaleDateString()}:`; | |
| priceDisplay.textContent = `$${price.toFixed(2)}`; | |
| historicDisplay.style.display = 'block'; | |
| } | |
| async fetchStockData(ticker) { | |
| // Simulate fetching company info | |
| const companyInfo = this._companyData.get(ticker) || { | |
| name: this.generateCompanyName(ticker), | |
| sector: this.generateSector() | |
| }; | |
| // Update company info display | |
| document.getElementById('companyName').textContent = companyInfo.name; | |
| document.getElementById('companyTicker').textContent = `(${ticker})`; | |
| // Generate realistic stock data | |
| this.generateStockData(ticker); | |
| this.generateNews(ticker, companyInfo.name); | |
| } | |
| generateCompanyName(ticker) { | |
| const suffixes = ['Inc.', 'Corporation', 'Company', 'Ltd.', 'Group']; | |
| const prefixes = ['Global', 'Advanced', 'United', 'National', 'International']; | |
| if (ticker.length <= 3) { | |
| return `${ticker.toUpperCase()} ${suffixes[Math.floor(Math.random() * suffixes.length)]}`; | |
| } else { | |
| return `${prefixes[Math.floor(Math.random() * prefixes.length)]} ${ticker.charAt(0) + ticker.slice(1).toLowerCase()} ${suffixes[Math.floor(Math.random() * suffixes.length)]}`; | |
| } | |
| } | |
| generateSector() { | |
| const sectors = ['Technology', 'Healthcare', 'Financial', 'Energy', 'Consumer Goods', 'Industrial', 'Utilities', 'Real Estate']; | |
| return sectors[Math.floor(Math.random() * sectors.length)]; | |
| } | |
| generateStockData(ticker) { | |
| // Generate realistic base price based on ticker | |
| let basePrice = 50 + (ticker.charCodeAt(0) % 26) * 10; | |
| const variation = (Math.random() - 0.5) * basePrice * 0.1; | |
| const currentPrice = basePrice + variation; | |
| const priceChange = (Math.random() - 0.5) * currentPrice * 0.05; | |
| const percentChange = (priceChange / currentPrice) * 100; | |
| this._currentPrice = currentPrice; | |
| this._priceChange = priceChange; | |
| // Update price display | |
| document.getElementById('currentPrice').textContent = `$${currentPrice.toFixed(2)}`; | |
| const priceChangeElement = document.getElementById('priceChange'); | |
| const changeText = `${priceChange >= 0 ? '+' : ''}${priceChange.toFixed(2)} (${percentChange >= 0 ? '+' : ''}${percentChange.toFixed(2)}%)`; | |
| priceChangeElement.textContent = changeText; | |
| priceChangeElement.className = `price-change ${priceChange >= 0 ? 'positive' : 'negative'}`; | |
| // Generate other metrics | |
| this.updateMetrics(currentPrice); | |
| } | |
| updateMetrics(currentPrice) { | |
| const marketCapMultiplier = 1 + Math.random() * 50; | |
| const volume = (5 + Math.random() * 15).toFixed(1); | |
| const metrics = { | |
| 'weekHigh': (currentPrice * (1.1 + Math.random() * 0.3)).toFixed(2), | |
| 'weekLow': (currentPrice * (0.7 + Math.random() * 0.2)).toFixed(2), | |
| 'marketCap': this.formatBillions(currentPrice * marketCapMultiplier), | |
| 'avgVolume': `${volume}M`, | |
| 'lastEPS': (Math.random() * 5).toFixed(2), | |
| 'estEPS': (Math.random() * 5).toFixed(2), | |
| 'revenue': this.formatBillions(10 + Math.random() * 90), | |
| 'nextEarnings': this.getNextEarningsDate(), | |
| 'peRatio': (10 + Math.random() * 25).toFixed(1), | |
| 'annualDiv': (Math.random() * 3).toFixed(2), | |
| 'divYield': (Math.random() * 8).toFixed(2) + '%', | |
| 'exDivDate': this.getRandomPastDate(), | |
| 'payDate': this.getRandomFutureDate(), | |
| 'payoutRatio': (20 + Math.random() * 60).toFixed(1) + '%', | |
| 'bookValue': (currentPrice * (0.5 + Math.random() * 0.7)).toFixed(2), | |
| 'priceToBook': (0.8 + Math.random() * 2).toFixed(2), | |
| 'roe': (5 + Math.random() * 15).toFixed(1) + '%', | |
| 'debtToEquity': (0.5 + Math.random() * 2).toFixed(2), | |
| 'beta': (0.5 + Math.random() * 1.5).toFixed(2), | |
| 'currentRatio': (0.5 + Math.random() * 2).toFixed(2), | |
| 'quickRatio': (0.3 + Math.random() * 1.5).toFixed(2), | |
| 'cashPerShare': (Math.random() * 10).toFixed(2), | |
| 'freeCashFlow': this.formatBillions(Math.random() * 20), | |
| 'creditRating': this.getRandomCreditRating() | |
| }; | |
| Object.keys(metrics).forEach(key => { | |
| const element = document.getElementById(key); | |
| if (element) { | |
| element.textContent = key.includes('weekHigh') || key.includes('weekLow') || key.includes('bookValue') || key.includes('cashPerShare') ? | |
| `$${metrics[key]}` : metrics[key]; | |
| } | |
| }); | |
| } | |
| generateNews(ticker, companyName) { | |
| const newsTemplates = [ | |
| `${companyName} Reports Strong Q2 Earnings`, | |
| `${ticker} Announces Strategic Partnership`, | |
| `Analysts Upgrade ${ticker} Stock Rating`, | |
| `${companyName} Expands Market Presence`, | |
| `${ticker} Declares Quarterly Dividend`, | |
| `New Product Launch from ${companyName}`, | |
| `${ticker} Stock Shows Resilience in Market Volatility`, | |
| `${companyName} CEO Discusses Future Strategy` | |
| ]; | |
| const newsContainer = document.getElementById('newsContainer'); | |
| newsContainer.innerHTML = ''; | |
| for (let i = 0; i < 4; i++) { | |
| const newsItem = document.createElement('div'); | |
| newsItem.className = 'news-item'; | |
| newsItem.innerHTML = ` | |
| <div class="news-title">${newsTemplates[Math.floor(Math.random() * newsTemplates.length)]}</div> | |
| <div class="news-date">${this.getRandomRecentDate()}</div> | |
| `; | |
| newsItem.addEventListener('click', () => this.showNewsDetail(newsItem)); | |
| newsContainer.appendChild(newsItem); | |
| } | |
| } | |
| getNextEarningsDate() { | |
| const today = new Date(); | |
| const nextEarnings = new Date(today); | |
| nextEarnings.setDate(today.getDate() + Math.floor(Math.random() * 90) + 15); | |
| return nextEarnings.toLocaleDateString(); | |
| } | |
| getRandomPastDate() { | |
| const today = new Date(); | |
| const pastDate = new Date(today); | |
| pastDate.setDate(today.getDate() - Math.floor(Math.random() * 90)); | |
| return pastDate.toLocaleDateString(); | |
| } | |
| getRandomFutureDate() { | |
| const today = new Date(); | |
| const futureDate = new Date(today); | |
| futureDate.setDate(today.getDate() + Math.floor(Math.random() * 60) + 15); | |
| return futureDate.toLocaleDateString(); | |
| } | |
| getRandomRecentDate() { | |
| const today = new Date(); | |
| const recentDate = new Date(today); | |
| recentDate.setDate(today.getDate() - Math.floor(Math.random() * 14)); | |
| return recentDate.toLocaleDateString(); | |
| } | |
| getRandomCreditRating() { | |
| const ratings = ['AAA', 'AA+', 'AA', 'AA-', 'A+', 'A', 'A-', 'BBB+', 'BBB', 'BBB-', 'BB+', 'BB']; | |
| return ratings[Math.floor(Math.random() * ratings.length)]; | |
| } | |
| formatBillions(value) { | |
| if (value >= 1000) { | |
| return `$${(value / 1000).toFixed(1)}T`; | |
| } | |
| return `$${value.toFixed(1)}B`; | |
| } | |
| showLoading(message) { | |
| document.getElementById('searchBtn').textContent = 'β³ Loading...'; | |
| document.getElementById('searchBtn').disabled = true; | |
| } | |
| hideWelcomeSection() { | |
| document.getElementById('welcomeSection').style.display = 'none'; | |
| } | |
| showDashboard() { | |
| document.getElementById('companyInfo').style.display = 'block'; | |
| document.getElementById('stockPrice').style.display = 'block'; | |
| document.getElementById('dashboardGrid').style.display = 'grid'; | |
| document.getElementById('refreshBtn').style.display = 'block'; | |
| document.getElementById('lastUpdated').style.display = 'block'; | |
| document.getElementById('searchBtn').textContent = 'π Search Stock'; | |
| document.getElementById('searchBtn').disabled = false; | |
| this.updateLastUpdatedTime(); | |
| } | |
| refreshData() { | |
| if (!this._currentTicker || this._isLoading) return; | |
| const refreshBtn = document.getElementById('refreshBtn'); | |
| refreshBtn.textContent = 'π Refreshing...'; | |
| refreshBtn.disabled = true; | |
| setTimeout(() => { | |
| this.generateStockData(this._currentTicker); | |
| this.updateLastUpdatedTime(); | |
| refreshBtn.textContent = 'π Refresh Data'; | |
| refreshBtn.disabled = false; | |
| this.showNotification('Data refreshed successfully!'); | |
| }, 1000); | |
| } | |
| updateLastUpdatedTime() { | |
| this._lastUpdateTime = new Date(); | |
| const lastUpdatedElement = document.getElementById('lastUpdated'); | |
| lastUpdatedElement.textContent = `Last updated: ${this._lastUpdateTime.toLocaleDateString()} at ${this._lastUpdateTime.toLocaleTimeString()}`; | |
| } | |
| showNotification(message) { | |
| const notification = document.createElement('div'); | |
| notification.style.cssText = ` | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| background: #27ae60; | |
| color: white; | |
| padding: 15px 20px; | |
| border-radius: 10px; | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.2); | |
| z-index: 1000; | |
| animation: slideIn 0.3s ease; | |
| `; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.style.animation = 'slideOut 0.3s ease'; | |
| setTimeout(() => { | |
| if (document.body.contains(notification)) { | |
| document.body.removeChild(notification); | |
| } | |
| }, 300); | |
| }, 3000); | |
| } | |
| showError(message) { | |
| const error = document.createElement('div'); | |
| error.className = 'error'; | |
| error.textContent = message; | |
| error.style.margin = '20px'; | |
| // Insert after search section | |
| const searchSection = document.querySelector('.search-section'); | |
| searchSection.insertAdjacentElement('afterend', error); | |
| setTimeout(() => { | |
| if (document.body.contains(error)) { | |
| error.remove(); | |
| } | |
| }, 5000); | |
| } | |
| showNewsDetail(newsItem) { | |
| const title = newsItem.querySelector('.news-title').textContent; | |
| const date = newsItem.querySelector('.news-date').textContent; | |
| alert(`News Detail:\n\n${title}\nPublished: ${date}\n\nThis would open a detailed view or redirect to the full article in a real implementation with news API integration.`); | |
| } | |
| delay(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| } | |
| // Add CSS animations | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes slideIn { | |
| from { transform: translateX(100%); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| @keyframes slideOut { | |
| from { transform: translateX(0); opacity: 1; } | |
| to { transform: translateX(100%); opacity: 0; } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| // Initialize dashboard when page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| window.universalStockDashboard = new UniversalStockDashboard(); | |
| }); | |
| // Global functions for button clicks | |
| function searchStock() { | |
| if (window.universalStockDashboard) { | |
| window.universalStockDashboard.searchStock(); | |
| } | |
| } | |
| function getHistoricPrice() { | |
| if (window.universalStockDashboard) { | |
| window.universalStockDashboard.getHistoricPrice(); | |
| } | |
| } | |
| function refreshData() { | |
| if (window.universalStockDashboard) { | |
| window.universalStockDashboard.refreshData(); | |
| } | |
| } | |
| function quickSearch(ticker) { | |
| if (window.universalStockDashboard) { | |
| window.universalStockDashboard.quickSearch(ticker); | |
| } | |
| } | |
| // Add smooth scrolling for better mobile experience | |
| document.documentElement.style.scrollBehavior = 'smooth'; | |
| // Add touch gesture support for mobile | |
| let _touchStartY = 0; | |
| document.addEventListener('touchstart', (e) => { | |
| _touchStartY = e.touches[0].clientY; | |
| }); | |
| document.addEventListener('touchend', (e) => { | |
| const touchEndY = e.changedTouches[0].clientY; | |
| const diff = _touchStartY - touchEndY; | |
| // Pull to refresh gesture (swipe down from top) | |
| if (diff < -100 && window.scrollY === 0) { | |
| refreshData(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment