feat: black unknown color and progressive report configuration UI
All checks were successful
Deployment / deploy-docker (push) Successful in 15s

This commit is contained in:
Melchior Reimers
2026-01-23 19:25:08 +01:00
parent 62994ee1b3
commit 5b712d8111

View File

@@ -91,7 +91,12 @@
.field-label { .field-label {
display: block; display: block;
text-xs font-bold text-slate-500 uppercase tracking-widest mb-3; font-size: 0.75rem;
font-weight: 700;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.75rem;
} }
.input-glass { .input-glass {
@@ -177,30 +182,34 @@
<!-- REPORT BUILDER VIEW --> <!-- REPORT BUILDER VIEW -->
<div id="view-analytics" class="view hidden flex-1 flex overflow-hidden"> <div id="view-analytics" class="view hidden flex-1 flex overflow-hidden">
<div class="config-sidebar p-8 space-y-8 overflow-y-auto bg-slate-900/30"> <!-- Configuration Sidebar -->
<h3 class="text-sky-400 font-bold text-xs uppercase tracking-tighter mb-4">Report Configuration</h3> <div class="config-sidebar p-8 space-y-6 overflow-y-auto bg-slate-900/30" id="reportConfig">
<h3 class="text-sky-400 font-bold text-xs uppercase tracking-tighter mb-4">Step-by-Step Configuration
</h3>
<!-- Time Selection --> <!-- Step 1: Time Range -->
<div> <div class="report-step" id="step1">
<label class="field-label">Time Period</label> <label class="field-label">1. Choose Analysis Period</label>
<select id="timeRangePreset" class="input-glass mb-2" onchange="handlePresetChange()"> <select id="timeRangePreset" class="input-glass" onchange="proceedToStep(2)">
<option value="" disabled selected>Select... </option>
<option value="1">Today</option> <option value="1">Today</option>
<option value="7" selected>Last 7 Days</option> <option value="7">Last 7 Days</option>
<option value="30">Last 30 Days</option> <option value="30">Last 30 Days</option>
<option value="ytd">Year to Date (YTD)</option> <option value="ytd">Year to Date (YTD)</option>
<option value="year">Full Year 2026</option> <option value="year">Full Year 2026</option>
<option value="custom">Custom Date Range...</option> <option value="custom">Custom Range...</option>
</select> </select>
<div id="customDates" class="hidden grid grid-cols-2 gap-2 mt-2"> <div id="customDates" class="hidden grid grid-cols-2 gap-2 mt-2">
<input type="date" id="dateFrom" class="input-glass text-xs p-2"> <input type="date" id="dateFrom" class="input-glass text-xs p-2" onchange="checkCustomDates()">
<input type="date" id="dateTo" class="input-glass text-xs p-2"> <input type="date" id="dateTo" class="input-glass text-xs p-2" onchange="checkCustomDates()">
</div> </div>
</div> </div>
<!-- Dimension Selection (X) --> <!-- Step 2: X-Axis -->
<div> <div class="report-step hidden opacity-50" id="step2">
<label class="field-label">Group By (X-Axis)</label> <label class="field-label">2. Primary Grouping (X-Axis)</label>
<select id="axisX" class="input-glass" onchange="updateSubGroupOptions()"> <select id="axisX" class="input-glass" onchange="proceedToStep(3); updateSubGroupOptions();">
<option value="" disabled selected>Select... </option>
<option value="day">Time (Daily)</option> <option value="day">Time (Daily)</option>
<option value="month">Time (Monthly)</option> <option value="month">Time (Monthly)</option>
<option value="exchange">Exchange Origin</option> <option value="exchange">Exchange Origin</option>
@@ -210,40 +219,45 @@
</select> </select>
</div> </div>
<!-- Breakdown Selection (Series) --> <!-- Step 3: Breakdown -->
<div id="subGroupContainer"> <div class="report-step hidden opacity-50" id="step3">
<label class="field-label">Breakdown by (Series)</label> <label class="field-label">3. Secondary Breakdown (Series)</label>
<select id="axisSub" class="input-glass"> <select id="axisSub" class="input-glass" onchange="proceedToStep(4)">
<option value="">None (Single Series)</option> <option value="">None (Unified)</option>
<option value="exchange">Exchange</option> <option value="exchange">By Exchange</option>
<option value="continent">Continent</option> <option value="continent">By Continent</option>
<option value="sector">Sector</option> <option value="sector">By Sector</option>
</select> </select>
</div> </div>
<!-- Metric Selection (Y) --> <!-- Step 4: Y-Axis -->
<div> <div class="report-step hidden opacity-50" id="step4">
<label class="field-label">Measurement (Y-Axis)</label> <label class="field-label">4. Select Metric (Y-Axis)</label>
<select id="axisY" class="input-glass"> <select id="axisY" class="input-glass" onchange="proceedToStep(5)">
<option value="" disabled selected>Select... </option>
<option value="volume">Trade Volume (€)</option> <option value="volume">Trade Volume (€)</option>
<option value="count">Number of Trades</option> <option value="count">Number of Trades</option>
<option value="avg_price">Average Price</option> <option value="avg_price">Average Performance</option>
</select> </select>
</div> </div>
<!-- Search & Filter --> <!-- Step 5: Filters -->
<div class="relative"> <div class="report-step hidden opacity-50" id="step5">
<label class="field-label">Filter to Companies</label> <div class="relative">
<input type="text" id="isinSearch" autocomplete="off" placeholder="Search ISIN or Name..." <label class="field-label">5. Optional Company Filters</label>
class="input-glass" onkeyup="handleSearch(event)"> <input type="text" id="isinSearch" autocomplete="off" placeholder="Search ISIN or Name..."
<div id="suggestions" class="suggestion-box hidden"></div> class="input-glass" onkeyup="handleSearch(event)">
<div id="filterChips" class="flex flex-wrap gap-2 mt-4"></div> <div id="suggestions" class="suggestion-box hidden"></div>
</div> <div id="filterChips" class="flex flex-wrap gap-2 mt-4"></div>
</div>
<div class="pt-4"> <div class="pt-8 space-y-3">
<button onclick="renderAnalyticsReport()" class="btn-primary w-full">GENERATE REPORT</button> <button onclick="renderAnalyticsReport()"
<p class="text-[10px] text-slate-500 mt-4 text-center">Charts are automatically optimized (Line vs class="btn-primary w-full shadow-sky-500/20">REGENERATE G-DRIVE REPORT</button>
Bar) based on selected grouping.</p> <button onclick="resetReportConfig()"
class="w-full text-xs text-slate-500 hover:text-white transition">Reset
Configuration</button>
</div>
</div> </div>
</div> </div>
@@ -481,22 +495,25 @@
const contCtx = document.getElementById('continentChart').getContext('2d'); const contCtx = document.getElementById('continentChart').getContext('2d');
if (charts.continent) charts.continent.destroy(); if (charts.continent) charts.continent.destroy();
const labels = store.summary.map(r => r[0]);
const baseColors = [
'#38bdf8', // Blue
'#f43f5e', // Red
'#10b981', // Green
'#fbbf24', // Yellow
'#8b5cf6', // Purple
'#f97316', // Orange
'#ec4899', // Pink
'#475569' // Slate
];
charts.continent = new Chart(contCtx, { charts.continent = new Chart(contCtx, {
type: 'doughnut', type: 'doughnut',
data: { data: {
labels: store.summary.map(r => r[0]), labels: labels,
datasets: [{ datasets: [{
data: store.summary.map(r => r[2]), data: store.summary.map(r => r[2]),
backgroundColor: [ backgroundColor: labels.map((l, i) => l.toLowerCase() === 'unknown' ? '#000000' : baseColors[i % baseColors.length]),
'#38bdf8', // Blue
'#f43f5e', // Red
'#10b981', // Green
'#fbbf24', // Yellow
'#8b5cf6', // Purple
'#f97316', // Orange
'#475569', // Slate (for Unknown - high contrast)
'#ec4899' // Pink
],
borderWidth: 0 borderWidth: 0
}] }]
}, },
@@ -504,6 +521,34 @@
}); });
} }
// --- Progressive Report Configuration ---
function proceedToStep(n) {
const step = document.getElementById(`step${n}`);
if (step) {
step.classList.remove('hidden');
setTimeout(() => step.classList.remove('opacity-50'), 50);
}
if (n === 2) handlePresetChange();
}
function checkCustomDates() {
const from = document.getElementById('dateFrom').value;
const to = document.getElementById('dateTo').value;
if (from && to) proceedToStep(2);
}
function resetReportConfig() {
document.querySelectorAll('.report-step').forEach((s, i) => {
if (i > 0) s.classList.add('hidden', 'opacity-50');
});
document.getElementById('timeRangePreset').value = '';
document.getElementById('axisX').value = '';
document.getElementById('axisSub').value = '';
document.getElementById('axisY').value = '';
store.pinnedIsins = [];
updateFilterChips();
}
function fillMetadataTable() { function fillMetadataTable() {
const tbody = document.getElementById('metadataRows'); const tbody = document.getElementById('metadataRows');
tbody.innerHTML = store.metadata.map(r => ` tbody.innerHTML = store.metadata.map(r => `