+60
-10
@@ -272,6 +272,10 @@ async function loadEvents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let allTags = [];
|
let allTags = [];
|
||||||
|
const PAGE_SIZE = 25;
|
||||||
|
let renderedCount = 0;
|
||||||
|
let filteredEvents = [];
|
||||||
|
let isLoadingMore = false;
|
||||||
|
|
||||||
async function loadAllTags() {
|
async function loadAllTags() {
|
||||||
try {
|
try {
|
||||||
@@ -290,24 +294,36 @@ function renderTimeline() {
|
|||||||
const tagFilter = document.getElementById('tagFilter').value;
|
const tagFilter = document.getElementById('tagFilter').value;
|
||||||
const search = document.getElementById('searchEvents').value.toLowerCase();
|
const search = document.getElementById('searchEvents').value.toLowerCase();
|
||||||
|
|
||||||
let filtered = events;
|
filteredEvents = events;
|
||||||
if (teamFilter) filtered = filtered.filter(e => e.team_id == teamFilter);
|
if (teamFilter) filteredEvents = filteredEvents.filter(e => e.team_id == teamFilter);
|
||||||
if (tagFilter) filtered = filtered.filter(e => e.tags && e.tags.includes(tagFilter));
|
if (tagFilter) filteredEvents = filteredEvents.filter(e => e.tags && e.tags.includes(tagFilter));
|
||||||
if (search) filtered = filtered.filter(e =>
|
if (search) filteredEvents = filteredEvents.filter(e =>
|
||||||
e.title.toLowerCase().includes(search) ||
|
e.title.toLowerCase().includes(search) ||
|
||||||
(e.description && e.description.toLowerCase().includes(search)) ||
|
(e.description && e.description.toLowerCase().includes(search)) ||
|
||||||
(e.tags && e.tags.some(t => t.includes(search)))
|
(e.tags && e.tags.some(t => t.includes(search)))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!filtered.length) {
|
renderedCount = 0;
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.removeAttribute('style');
|
||||||
|
renderMoreEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMoreEvents() {
|
||||||
|
const container = document.getElementById('timelineContainer');
|
||||||
|
|
||||||
|
if (!filteredEvents.length) {
|
||||||
container.innerHTML = '<div class="text-center text-secondary py-5"><i class="fas fa-book-open fs-1 mb-2"></i><p>No events yet. Create your first incident entry!</p></div>';
|
container.innerHTML = '<div class="text-center text-secondary py-5"><i class="fas fa-book-open fs-1 mb-2"></i><p>No events yet. Create your first incident entry!</p></div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML = filtered.map(e => {
|
const next = Math.min(renderedCount + PAGE_SIZE, filteredEvents.length);
|
||||||
|
const batch = filteredEvents.slice(renderedCount, next);
|
||||||
|
|
||||||
|
const html = batch.map(e => {
|
||||||
const date = new Date(e.occurred_at).toLocaleString();
|
const date = new Date(e.occurred_at).toLocaleString();
|
||||||
return `
|
return `
|
||||||
<div class="timeline-item">
|
<div class="timeline-item" data-event-id="${e.id}">
|
||||||
<div class="timeline-dot severity-${e.severity}"></div>
|
<div class="timeline-dot severity-${e.severity}"></div>
|
||||||
<div class="card timeline-card bg-dark border-secondary" style="border-left-color: ${e.team_color}">
|
<div class="card timeline-card bg-dark border-secondary" style="border-left-color: ${e.team_color}">
|
||||||
<div class="card-body py-1 px-3">
|
<div class="card-body py-1 px-3">
|
||||||
@@ -341,10 +357,44 @@ function renderTimeline() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
filtered.forEach(e => {
|
const wrapper = document.createElement('div');
|
||||||
const container = document.getElementById('attachments-' + e.id);
|
wrapper.innerHTML = html;
|
||||||
if (container) renderAttachments(e.id, container);
|
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
while (wrapper.firstChild) fragment.appendChild(wrapper.firstChild);
|
||||||
|
container.appendChild(fragment);
|
||||||
|
|
||||||
|
renderedCount = next;
|
||||||
|
|
||||||
|
batch.forEach(e => {
|
||||||
|
const el = document.querySelector(`.timeline-item[data-event-id="${e.id}"] .attachments-list`);
|
||||||
|
if (el) renderAttachments(e.id, el);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (renderedCount < filteredEvents.length) {
|
||||||
|
const sentinel = document.createElement('div');
|
||||||
|
sentinel.id = 'timeline-sentinel';
|
||||||
|
sentinel.className = 'text-center py-3';
|
||||||
|
sentinel.innerHTML = '<button class="btn btn-outline-primary btn-sm" onclick="renderMoreEvents()"><i class="fas fa-chevron-down me-1"></i>Load more (' + (filteredEvents.length - renderedCount) + ' remaining)</button>';
|
||||||
|
container.appendChild(sentinel);
|
||||||
|
observeSentinel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sentinelObserver = null;
|
||||||
|
|
||||||
|
function observeSentinel() {
|
||||||
|
if (sentinelObserver) sentinelObserver.disconnect();
|
||||||
|
const sentinel = document.getElementById('timeline-sentinel');
|
||||||
|
if (!sentinel) return;
|
||||||
|
sentinelObserver = new IntersectionObserver((entries) => {
|
||||||
|
if (entries[0].isIntersecting && !isLoadingMore) {
|
||||||
|
isLoadingMore = true;
|
||||||
|
renderMoreEvents();
|
||||||
|
isLoadingMore = false;
|
||||||
|
}
|
||||||
|
}, { rootMargin: '200px' });
|
||||||
|
sentinelObserver.observe(sentinel);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDocLinks(text) {
|
function renderDocLinks(text) {
|
||||||
|
|||||||
Reference in New Issue
Block a user