diff --git a/web/js/events.js b/web/js/events.js
index d5d54f6..91c96b2 100644
--- a/web/js/events.js
+++ b/web/js/events.js
@@ -2,56 +2,80 @@ import { apiFetch } from "./api.js";
import { dom } from "./dom.js";
import { state } from "./state.js";
+const PAGE_SIZE = 10;
+
+let _page = 1;
+let _hasMore = false;
+let _loading = false;
+
function formatTime(value) {
- if (!value) {
- return "--";
- }
- return value;
+ return value || "--";
}
-export function renderEvents() {
- dom.eventList.innerHTML = "";
-
- if (!state.events.length) {
- dom.eventList.innerHTML = '
';
- return;
- }
-
- state.events.forEach((item) => {
- const row = document.createElement("div");
- row.className = "event-card";
- row.innerHTML = `${(item.level || "info").toUpperCase()}${formatTime(item.created_at)}${item.event_type}
${item.message}
`;
- dom.eventList.appendChild(row);
- });
+function makeCard(item) {
+ const row = document.createElement("div");
+ row.className = "event-card";
+ row.innerHTML = `${(item.level || "info").toUpperCase()}${formatTime(item.created_at)}${item.event_type}
${item.message}
`;
+ return row;
}
-function matchesCurrentFilter(item) {
- if (state.selectedUnitId && item.unit_id !== state.selectedUnitId) {
- return false;
- }
- return true;
-}
+async function loadMore() {
+ if (_loading || !_hasMore) return;
+ _loading = true;
-export function prependEvent(item) {
- if (!matchesCurrentFilter(item)) {
- return;
- }
+ const params = new URLSearchParams({ page: String(_page), page_size: String(PAGE_SIZE) });
+ if (state.selectedUnitId) params.set("unit_id", state.selectedUnitId);
- state.events = [item, ...state.events.filter((existing) => existing.id !== item.id)].slice(0, 20);
- renderEvents();
+ try {
+ const response = await apiFetch(`/api/event?${params.toString()}`);
+ const items = response.data || [];
+ items.forEach((item) => dom.eventList.appendChild(makeCard(item)));
+ _hasMore = items.length === PAGE_SIZE;
+ _page += 1;
+ } finally {
+ _loading = false;
+ }
}
export async function loadEvents() {
- const params = new URLSearchParams({
- page: "1",
- page_size: "20",
- });
+ _page = 1;
+ _hasMore = false;
+ _loading = false;
+ dom.eventList.innerHTML = "";
- if (state.selectedUnitId) {
- params.set("unit_id", state.selectedUnitId);
+ const params = new URLSearchParams({ page: "1", page_size: String(PAGE_SIZE) });
+ if (state.selectedUnitId) params.set("unit_id", state.selectedUnitId);
+
+ _loading = true;
+ try {
+ const response = await apiFetch(`/api/event?${params.toString()}`);
+ const items = response.data || [];
+
+ if (!items.length) {
+ dom.eventList.innerHTML = '';
+ return;
+ }
+
+ items.forEach((item) => dom.eventList.appendChild(makeCard(item)));
+ _hasMore = items.length === PAGE_SIZE;
+ _page = 2;
+ } finally {
+ _loading = false;
}
-
- const response = await apiFetch(`/api/event?${params.toString()}`);
- state.events = response.data || [];
- renderEvents();
}
+
+export function prependEvent(item) {
+ if (state.selectedUnitId && item.unit_id !== state.selectedUnitId) return;
+
+ const placeholder = dom.eventList.querySelector(".list-item");
+ if (placeholder) placeholder.remove();
+
+ dom.eventList.insertBefore(makeCard(item), dom.eventList.firstChild);
+}
+
+dom.eventList.addEventListener("scroll", () => {
+ const el = dom.eventList;
+ if (el.scrollTop + el.clientHeight >= el.scrollHeight - 40) {
+ loadMore();
+ }
+});