feat: advanced analytics with deep linking, URL params, and progressive UI
All checks were successful
Deployment / deploy-docker (push) Successful in 16s
All checks were successful
Deployment / deploy-docker (push) Successful in 16s
This commit is contained in:
@@ -312,7 +312,12 @@
|
||||
const titles = { 'dashboard': 'Market Overview', 'analytics': 'Custom Report Builder', 'metadata': 'Entity Metadata' };
|
||||
document.getElementById('pageTitle').innerText = titles[viewId];
|
||||
|
||||
if (viewId === 'analytics') renderAnalyticsReport(); else fetchData();
|
||||
if (viewId === 'analytics') {
|
||||
// If it's a fresh visit without params, we might want to reset or keep state
|
||||
// renderAnalyticsReport();
|
||||
} else {
|
||||
fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
function handlePresetChange() {
|
||||
@@ -323,12 +328,11 @@
|
||||
function updateSubGroupOptions() {
|
||||
const x = document.getElementById('axisX').value;
|
||||
const sub = document.getElementById('axisSub');
|
||||
const container = document.getElementById('subGroupContainer');
|
||||
|
||||
// Contextual Logic: If X is already a metadata field, don't allow it as series (too complex)
|
||||
container.classList.remove('hidden');
|
||||
// Contextual Logic: If X is already a metadata field, don't allow it as series
|
||||
Array.from(sub.options).forEach(opt => {
|
||||
opt.disabled = (opt.value === x);
|
||||
if (opt.value === x && sub.value === x) sub.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -357,9 +361,9 @@
|
||||
if (!store.pinnedIsins.find(p => p.isin === isin)) {
|
||||
store.pinnedIsins.push({ isin, name });
|
||||
updateFilterChips();
|
||||
// Close search
|
||||
document.getElementById('isinSearch').value = '';
|
||||
document.getElementById('suggestions').classList.add('hidden');
|
||||
if (window.activeView === 'analytics') proceedToStep(5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +380,8 @@
|
||||
${p.isin} <span class="ml-2 cursor-pointer text-slate-500 hover:text-white" onclick="removeFilter('${p.isin}')">×</span>
|
||||
</div>
|
||||
`).join('');
|
||||
container.innerHTML = html;
|
||||
pins.innerHTML = html;
|
||||
if (container) container.innerHTML = html;
|
||||
if (pins) pins.innerHTML = html;
|
||||
}
|
||||
|
||||
function getDates() {
|
||||
@@ -424,6 +428,9 @@
|
||||
const y = document.getElementById('axisY').value;
|
||||
const isins = store.pinnedIsins.map(p => p.isin).join(',');
|
||||
|
||||
// Validate that basic steps are done
|
||||
if (!dates.from || !x || !y) return;
|
||||
|
||||
let url = `${API}/analytics?metric=${y}&group_by=${x}`;
|
||||
if (sub) url += `&sub_group_by=${sub}`;
|
||||
if (dates.from) url += `&date_from=${dates.from}`;
|
||||
@@ -504,7 +511,7 @@
|
||||
'#8b5cf6', // Purple
|
||||
'#f97316', // Orange
|
||||
'#ec4899', // Pink
|
||||
'#475569' // Slate
|
||||
'#6366f1' // Indigo
|
||||
];
|
||||
|
||||
charts.continent = new Chart(contCtx, {
|
||||
@@ -523,12 +530,18 @@
|
||||
|
||||
// --- Progressive Report Configuration ---
|
||||
function proceedToStep(n) {
|
||||
// First, enable/show the step
|
||||
const step = document.getElementById(`step${n}`);
|
||||
if (step) {
|
||||
step.classList.remove('hidden');
|
||||
setTimeout(() => step.classList.remove('opacity-50'), 50);
|
||||
}
|
||||
|
||||
// Contextual Visibility Logic: If we are at step 2, handle custom date toggles
|
||||
if (n === 2) handlePresetChange();
|
||||
|
||||
// Auto-scroll logic if sidebar is long
|
||||
if (n > 2) step.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
function checkCustomDates() {
|
||||
@@ -552,7 +565,7 @@
|
||||
function fillMetadataTable() {
|
||||
const tbody = document.getElementById('metadataRows');
|
||||
tbody.innerHTML = store.metadata.map(r => `
|
||||
<tr class="hover:bg-white/5 transition border-b border-white/5 cursor-pointer" onclick="addFilter('${r[0]}', '${r[1]}'); showView('analytics');">
|
||||
<tr class="hover:bg-white/5 transition border-b border-white/5 cursor-pointer" onclick="deepLinkToAnalytics('${r[0]}', '${r[1]}')">
|
||||
<td class="p-5 px-8 font-mono text-sky-400 font-bold">${r[0]}</td>
|
||||
<td class="p-5 font-semibold text-slate-200">${r[1]}</td>
|
||||
<td class="p-5 text-slate-500">${r[2]}</td>
|
||||
@@ -561,12 +574,51 @@
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function deepLinkToAnalytics(isin, name) {
|
||||
// Set values as requested: Time: 30d, X: Day, Y: Volume, Filter: ISIN
|
||||
resetReportConfig();
|
||||
document.getElementById('timeRangePreset').value = '30';
|
||||
document.getElementById('axisX').value = 'day';
|
||||
document.getElementById('axisSub').value = '';
|
||||
document.getElementById('axisY').value = 'volume';
|
||||
|
||||
store.pinnedIsins = [{ isin, name }];
|
||||
updateFilterChips();
|
||||
|
||||
// Show all steps progressively for the user
|
||||
for (let i = 1; i <= 5; i++) proceedToStep(i);
|
||||
|
||||
showView('analytics');
|
||||
renderAnalyticsReport();
|
||||
}
|
||||
|
||||
function handleUrlParams() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const view = params.get('view');
|
||||
const isin = params.get('isin');
|
||||
if (view) showView(view);
|
||||
if (view === 'analytics' && isin) {
|
||||
// If isin is provided in URL, we wait for metadata to be fetched then deep link
|
||||
const checkStore = setInterval(() => {
|
||||
if (store.metadata.length > 0) {
|
||||
const entity = store.metadata.find(r => r[0] === isin);
|
||||
if (entity) deepLinkToAnalytics(isin, entity[1]);
|
||||
clearInterval(checkStore);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function filterMetadata(q) {
|
||||
const rows = document.querySelectorAll('#metadataRows tr');
|
||||
rows.forEach(r => r.style.display = r.innerText.toLowerCase().includes(q.toLowerCase()) ? '' : 'none');
|
||||
}
|
||||
|
||||
window.onload = () => { fetchData(); setInterval(fetchData, 30000); };
|
||||
window.onload = async () => {
|
||||
await fetchData();
|
||||
handleUrlParams();
|
||||
setInterval(fetchData, 30000);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user