const state = { user: null, logs: [], logsLoaded: false, initialLogRetryCount: 0, searchActivated: false }; const elements = { welcomeText: document.getElementById("welcomeText"), routeText: document.getElementById("routeText"), logoutButton: document.getElementById("logoutButton"), entriesList: document.getElementById("entriesList"), searchInput: document.getElementById("searchInput"), sortSelect: document.getElementById("sortSelect") }; bindEvents(); bootPage(); function bindEvents() { elements.logoutButton.addEventListener("click", logout); elements.searchInput.addEventListener("input", handleSearchInput); elements.sortSelect.addEventListener("change", loadLogs); window.addEventListener("pageshow", handlePageShow); } async function bootPage() { state.logsLoaded = false; state.initialLogRetryCount = 0; state.searchActivated = false; elements.searchInput.value = ""; renderLogs(); try { const response = await apiFetch("/api/me"); state.user = response.user; fillUserHeader(); await loadLogs(true); } catch (error) { if (error.status === 401) { window.location.href = "/bejelentkezes"; return; } elements.welcomeText.textContent = "Betöltési hiba"; elements.routeText.textContent = "A napló nem tölthető be. Frissítsd az oldalt vagy jelentkezz be újra."; renderError(error.message); } } function fillUserHeader() { const name = state.user?.fullName || "Felhasználó"; const route = state.user?.routeName || "Nincs megadva"; elements.welcomeText.textContent = "Napló"; elements.routeText.textContent = `${name} · Technikusi útvonal: ${route}`; } async function loadLogs(isInitialLoad = false) { try { const sortBy = elements.sortSelect.value || "inspectionDateDesc"; const response = await apiFetch(`/api/logs?sortBy=${encodeURIComponent(sortBy)}`); state.logs = Array.isArray(response.logs) ? response.logs.map((log) => ({ ...log, files: Array.isArray(log.files) ? log.files : [] })) : []; state.logsLoaded = true; renderLogs(); if (isInitialLoad && !state.logs.length) { scheduleInitialLogRetry(); } } catch (error) { state.logs = []; state.logsLoaded = true; renderError(error.message); if (isInitialLoad) { scheduleInitialLogRetry(); } } } function renderLogs() { if (!state.logsLoaded) { elements.entriesList.innerHTML = `

Betöltés

A bejegyzések betöltése folyamatban van.

`; return; } const query = state.searchActivated ? elements.searchInput.value.trim().toLowerCase() : ""; const visibleLogs = state.logs.filter((log) => searchableLog(log).includes(query)); if (!visibleLogs.length) { elements.entriesList.innerHTML = `

Nincs találat

Módosítsd a keresést, vagy hozz létre új ellenőrzési bejegyzést az alkalmazás oldalon.

`; return; } elements.entriesList.innerHTML = visibleLogs .map((log) => { const fileMarkup = log.files.length ? log.files .map( (file) => ` ${escapeHtml(file.originalName)} ` ) .join("") : `Nincs PDF csatolmány`; return `
${humanizeStatus(log.status)} ${formatDate(log.inspectionDate)}

${escapeHtml(log.elevatorName)}

Gyári szám: ${escapeHtml(log.serialNumber)}

Helyszín: ${escapeHtml(log.location)}

Útvonal: ${escapeHtml(log.routeName)}

Technikus: ${escapeHtml(log.technicianName)}

Következő ellenőrzés: ${formatDate(log.nextInspectionDate)}

Megjegyzés: ${escapeHtml(log.notes || "Nincs megjegyzés.")}

${fileMarkup}
`; }) .join(""); } function renderError(message) { elements.entriesList.innerHTML = `

Hiba

${escapeHtml(message)}

`; } function scheduleInitialLogRetry() { if (state.initialLogRetryCount >= 3 || state.logs.length) { return; } state.initialLogRetryCount += 1; window.setTimeout(() => { state.logsLoaded = false; renderLogs(); loadLogs(false); }, 500 * state.initialLogRetryCount); } function handleSearchInput() { state.searchActivated = true; renderLogs(); } function handlePageShow() { if (state.user) { state.logsLoaded = false; renderLogs(); loadLogs(); } } async function logout() { try { const response = await apiFetch("/api/logout", { method: "POST" }); window.location.href = response.redirectPath; } catch (_error) { window.location.href = "/bejelentkezes"; } } async function apiFetch(url, options = {}) { const headers = options.isFormData ? {} : { "Content-Type": "application/json" }; const response = await fetch(url, { method: options.method || "GET", credentials: "include", headers, body: options.body }); const contentType = response.headers.get("content-type") || ""; const data = contentType.includes("application/json") ? await response.json() : { message: "A szerver nem JSON választ adott vissza." }; if (!response.ok) { const error = new Error(data.message || "A kérés nem sikerült."); error.status = response.status; throw error; } return data; } function searchableLog(log) { return [ log.elevatorName, log.serialNumber, log.location, log.routeName, log.technicianName, log.status, log.notes ] .join(" ") .toLowerCase(); } function humanizeStatus(status) { return status === "rendben" ? "Rendben" : status === "figyelmeztetes" ? "Figyelmeztetés" : "Lejárt"; } function statusClassName(status) { return status === "rendben" ? "success-chip" : status === "figyelmeztetes" ? "warning-chip" : "danger-chip"; } function formatDate(value) { const normalizedDate = normalizeDateInput(value); return normalizedDate ? new Date(`${normalizedDate}T00:00:00`).toLocaleDateString("hu-HU") : "Nincs dátum"; } function normalizeDateInput(value) { if (!value) { return ""; } if (typeof value === "string") { return value.includes("T") ? value.slice(0, 10) : value; } const parsedDate = new Date(value); return Number.isNaN(parsedDate.getTime()) ? "" : parsedDate.toISOString().slice(0, 10); } function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); }