feat: improve timeline/admin flows and ingest projection repairs

This commit is contained in:
2026-03-01 00:21:31 +03:00
parent c805daabcf
commit 229db81229
26 changed files with 2873 additions and 270 deletions

View File

@@ -67,9 +67,21 @@
</section>
<section class="card">
<h2>Timeline</h2>
<div id="component-timeline-panel-{{.Component.ID}}" class="timeline-panel"></div>
<div class="meta">Timeline groups repeated events and lets you drill down to individual history entries.</div>
<div class="button-row" style="justify-content: space-between; margin-bottom: 12px;">
<h2 style="margin:0;">Activity</h2>
<div class="button-row">
<button class="button" id="component-activity-tab-timeline" type="button">Movement & Events</button>
<button class="button button-secondary" id="component-activity-tab-imports" type="button">Import History</button>
</div>
</div>
<div id="component-activity-view-timeline">
<div id="component-timeline-panel-{{.Component.ID}}" class="timeline-panel"></div>
<div class="meta">Timeline groups repeated events and lets you drill down to individual history entries.</div>
</div>
<div id="component-activity-view-imports" hidden>
<div id="component-imports-panel-{{.Component.ID}}" class="timeline-panel"></div>
<div class="meta">Import history shows upload type, upload time, and import metadata.</div>
</div>
</section>
</main>
<script>
@@ -79,6 +91,23 @@
apiBase: '/api/history/components/{{.Component.ID}}/timeline',
detailBase: '/api/history/components/{{.Component.ID}}/events'
});
initImportHistoryPanel({
rootId: 'component-imports-panel-{{.Component.ID}}',
apiBase: '/components/{{.Component.ID}}/imports'
});
const tabTimeline = document.getElementById('component-activity-tab-timeline');
const tabImports = document.getElementById('component-activity-tab-imports');
const viewTimeline = document.getElementById('component-activity-view-timeline');
const viewImports = document.getElementById('component-activity-view-imports');
function setComponentActivityTab(tab) {
const timelineActive = tab === 'timeline';
if (viewTimeline) viewTimeline.hidden = !timelineActive;
if (viewImports) viewImports.hidden = timelineActive;
if (tabTimeline) tabTimeline.className = timelineActive ? 'button' : 'button button-secondary';
if (tabImports) tabImports.className = timelineActive ? 'button button-secondary' : 'button';
}
if (tabTimeline) tabTimeline.addEventListener('click', () => setComponentActivityTab('timeline'));
if (tabImports) tabImports.addEventListener('click', () => setComponentActivityTab('imports'));
});
</script>
<script>