Skip to content

Instantly share code, notes, and snippets.

@xbalajipge
Created June 18, 2025 05:54
Show Gist options
  • Select an option

  • Save xbalajipge/415c92b6e3d7535ac6e78bf11ded6b9b to your computer and use it in GitHub Desktop.

Select an option

Save xbalajipge/415c92b6e3d7535ac6e78bf11ded6b9b to your computer and use it in GitHub Desktop.
html-universal-stock-dashboard.html
<!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