built flexible colunm and hortizontial scrolling

This commit is contained in:
2026-02-06 02:00:43 +08:00
parent b673b917fa
commit 2d9fa9a47b
+89 -73
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v=1"> <link rel="manifest" href="{{ url_for('static', filename='site.webmanifest') }}?v=1">
<link rel="apple-touch-icon" href="{{ url_for('static', filename='apple-touch-icon.png') }}?v=1"> <link rel="apple-touch-icon" href="{{ url_for('static', filename='apple-touch-icon.png') }}?v=1">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='favicon-32x32.png') }}?v=1"> <link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='favicon-32x32.png') }}?v=1">
@@ -16,6 +16,8 @@
--header-bg: #4a5568; --header-bg: #4a5568;
--text-up: #28a745; --text-up: #28a745;
--text-down: #dc3545; --text-down: #dc3545;
--sticky-bg: #ffffff;
--sticky-bg-alt: #f9f9f9; /* For zebra stripes */
} }
body { body {
@@ -25,7 +27,10 @@
padding: 10px; padding: 10px;
} }
/* Card Styling */
.card { .card {
/*max-width: 1100px; /* Limits how wide the table grows on a desktop */
/*margin: 0 auto; /* Centers the table on the screen */
border: none; border: none;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1); box-shadow: 0 4px 15px rgba(0,0,0,0.1);
@@ -38,14 +43,25 @@
padding: 15px; padding: 15px;
} }
/* Zebra Striping Logic */ /* 1. Fixed Table Layout Strategy */
.table-striped tbody tr:nth-of-type(odd) { .table-responsive {
background-color: rgba(0, 0, 0, 0.03); border-radius: 8px;
overflow-x: auto;
} }
/* Sticky Column for Mobile Scrolling */ .table {
.table-responsive { border-radius: 8px; } /* table-layout: auto; */ /* Default - let it be auto for data safety */
width: 100%;
/* This MUST match the sum of your column min-widths */
min-width: 600px;
/* Critical for the 'Sticky' column borders to look right */
border-collapse: separate;
border-spacing: 0;
}
/* 2. Header & Cell Styling */
.table thead th { .table thead th {
background-color: var(--header-bg) !important; background-color: var(--header-bg) !important;
color: white !important; color: white !important;
@@ -53,9 +69,9 @@
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
text-align: center; text-align: center;
white-space: nowrap;
padding: 12px 8px; padding: 12px 8px;
border: none; border: none;
white-space: nowrap;
} }
.table td { .table td {
@@ -63,81 +79,72 @@
vertical-align: middle; vertical-align: middle;
font-size: 0.85rem; font-size: 0.85rem;
white-space: nowrap; white-space: nowrap;
border-color: #f1f1f1; border-bottom: 1px solid #f1f1f1;
padding: 10px 8px;
} }
/* Keep Instrument Name fixed on the left while swiping */ /* 3. Sticky Column Logic (Instrument) */
.table td:first-child, .table th:first-child { .table td:first-child,
.table th:first-child {
position: sticky; position: sticky;
text-align: left !important; /* Force left alignment */
padding-left: 15px; /* Add space so text doesn't touch the edge */
left: 0; left: 0;
z-index: 10; z-index: 10;
text-align: left; background-color: var(--sticky-bg);
min-width: 140px;
background-color: white;
font-weight: 700; font-weight: 700;
/* THE SHADOW EFFECT */
/* This adds a 4px blur shadow to the right side of the column */
box-shadow: 4px 0 8px -2px rgba(0, 0, 0, 0.15);
/* Clean border to define the edge */
border-right: 1px solid #ddd;
}
/* 4. Zebra Striping + Sticky Fix */
.table-striped tbody tr:nth-of-type(odd) {
background-color: rgba(0, 0, 0, 0.03);
} }
/* Ensure zebra stripe works on sticky column */
.table-striped tbody tr:nth-of-type(odd) td:first-child { .table-striped tbody tr:nth-of-type(odd) td:first-child {
background-color: #f9f9f9; background-color: var(--sticky-bg-alt); /* Matches stripe color */
}
/* Custom UI Elements */
.btn-refresh {
background-color: #0d6efd;
color: white !important;
font-weight: 600;
border-radius: 6px;
padding: 6px 16px;
transition: 0.2s;
} }
/* Custom Colors & UI */
.text-up { color: var(--text-up); font-weight: 600; } .text-up { color: var(--text-up); font-weight: 600; }
.text-down { color: var(--text-down); font-weight: 600; } .text-down { color: var(--text-down); font-weight: 600; }
@media (prefers-color-scheme: dark) {
:root {
--header-bg: #1a202c; /* Darker header */
--sticky-bg: #2d3748; /* Dark background for sticky column */
--sticky-bg-alt: #1a202c;
background-color: #121212; /* Main page background */
color: #e2e8f0; /* Light text */
}
.card, .card-header {
background-color: #2d3748 !important;
color: white;
}
.table td { border-color: #4a5568; color: #e2e8f0; }
}
.badge-kd {
background-color: #6c757d;
font-size: 0.75rem;
padding: 5px 8px;
}
#loading { display: none; margin-left: 10px; font-size: 0.8rem; color: #666; } #loading { display: none; margin-left: 10px; font-size: 0.8rem; color: #666; }
/* 1. Ensure the 52W column is wide enough for the progress bar */
.table td:nth-child(4), .table th:nth-child(4) {
min-width: 140px;
text-align: left;
}
/* 2. Narrow the EMA and K/D columns since they only have small numbers */
.table td:nth-child(n+5), .table th:nth-child(n+5) {
min-width: 75px;
}
/* 3. Give the Instrument column a bit more breathing room if names are long */
.table td:first-child, .table th:first-child {
min-width: 180px; /* Increased from 140px */
}
</style> </style>
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<div class="container-fluid"> <div class="container-fluid">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<i class="bi bi-graph-up-arrow me-2"></i>Finance Suite <i class="bi bi-graph-up-arrow me-2"></i>Finance Suite
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-text text-light small d-none d-md-inline">
System Status: <span id="statusIndicator" class="text-warning">● Connecting...</span>
</span>
</button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item"> <li class="nav-item"><a class="nav-link" href="/">Dashboard</a></li>
<a class="nav-link" href="/">Dashboard</a> <li class="nav-item"><a class="nav-link" href="/backtest">Backtester</a></li>
</li>
<li class="nav-item">
<a class="nav-link" href="/backtest">Backtester</a>
</li>
</ul> </ul>
</div> </div>
<span class="navbar-text text-light small d-none d-md-inline"> <span class="navbar-text text-light small d-none d-md-inline">
@@ -145,19 +152,18 @@
</span> </span>
</div> </div>
</nav> </nav>
<div class="container-fluid p-0"> <div class="container-fluid p-0">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0 text-dark">Portfolio Signals</h5> <h5 class="mb-0 text-dark">Portfolio Signals</h5>
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<span id="loading" class="me-2" style="display:none;">Updating...</span> <span id="loading">Updating...</span>
<button class="btn btn-outline-secondary btn-sm" onclick="loadData()"> <button class="btn btn-outline-secondary btn-sm" onclick="loadData()">
<i class="bi bi-arrow-clockwise"></i> Refresh Table <i class="bi bi-arrow-clockwise"></i> Refresh
</button> </button>
<button id="syncBtn" class="btn btn-primary btn-sm" onclick="runGlobalSync()"> <button id="syncBtn" class="btn btn-primary btn-sm" onclick="runGlobalSync()">
<i class="bi bi-cloud-download"></i> Sync New Data <i class="bi bi-cloud-download"></i> Sync Data
</button> </button>
</div> </div>
</div> </div>
@@ -166,18 +172,20 @@
<table class="table table-hover table-striped mb-0"> <table class="table table-hover table-striped mb-0">
<thead> <thead>
<tr> <tr>
<th style="width: 25%;">Instrument</th> <th style="min-width: 50px;">Instrument</th>
<th style="width: 10%;">Date</th> <th style="width: 10%;">Close</th> <th style="min-width: 25px;">Date</th>
<th style="width: 10%;">Chg%</th> <th style="min-width: 25px;">Close</th>
<th style="width: 10%;">52W Range</th> <th style="width: 8%;">20 EMA</th> <th style="min-width: 25px;">Chg%</th>
<th style="width: 10%;">50 EMA</th> <th style="min-width: 100px;">52W Range</th>
<th style="width: 10%;">100 EMA</th> <th style="min-width: 85px;">20 EMA</th>
<th style="width: 10%;">200 EMA</th> <th style="min-width: 25px;">50 EMA</th>
<th style="width: 5%;">K/D</th> <th style="min-width: 25px;">100 EMA</th>
<th style="min-width: 25px;">200 EMA</th>
<th style="min-width: 60px;">K/D</th>
</tr> </tr>
</thead> </thead>
<tbody id="tableBody"> <tbody id="tableBody">
<tr><td colspan="9" class="p-4">Initializing data engine...</td></tr> <tr><td colspan="10" class="p-4">Initializing data engine...</td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -241,8 +249,16 @@
} }
let displayDate = "N/A"; let displayDate = "N/A";
if (item.last_date && item.last_date.includes('-')) { if (item.last_date && item.last_date.includes('-')) {
const parts = item.last_date.split('-'); // Splits "2026-01-30" into ["2026", "01", "30"] const parts = item.last_date.split('-');
displayDate = `${parts[2]}/${parts[1]}`; // Combines into "30/01" const formatted = `${parts[2]}/${parts[1]}`;
// Get today's date in YYYY-MM-DD format to compare
const today = new Date().toISOString().split('T')[0];
// Pick color based on freshness
const color = (item.last_date === today) ? '#28a745' : '#dc3545';
displayDate = `<span style="color: ${color};">${formatted}</span>`;
} }
// --- 2. Calculations & Styling --- // --- 2. Calculations & Styling ---
const current = parseFloat(item.last_close) || 0; const current = parseFloat(item.last_close) || 0;