liftman/public/logs.js
2026-05-22 23:05:37 +02:00

262 lines
7.1 KiB
JavaScript

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 = `
<article class="entry-card empty-card">
<h3>Betöltés</h3>
<p>A bejegyzések betöltése folyamatban van.</p>
</article>
`;
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 = `
<article class="entry-card empty-card">
<h3>Nincs találat</h3>
<p>Módosítsd a keresést, vagy hozz létre új ellenőrzési bejegyzést az alkalmazás oldalon.</p>
</article>
`;
return;
}
elements.entriesList.innerHTML = visibleLogs
.map((log) => {
const fileMarkup = log.files.length
? log.files
.map(
(file) => `
<a class="file-link" href="${file.downloadPath}">
${escapeHtml(file.originalName)}
</a>
`
)
.join("")
: `<span class="muted-text">Nincs PDF csatolmány</span>`;
return `
<article class="entry-card">
<div class="card-topline">
<span class="status-chip ${statusClassName(log.status)}">${humanizeStatus(log.status)}</span>
<span class="muted-text">${formatDate(log.inspectionDate)}</span>
</div>
<h3>${escapeHtml(log.elevatorName)}</h3>
<p><strong>Gyári szám:</strong> ${escapeHtml(log.serialNumber)}</p>
<p><strong>Helyszín:</strong> ${escapeHtml(log.location)}</p>
<p><strong>Útvonal:</strong> ${escapeHtml(log.routeName)}</p>
<p><strong>Technikus:</strong> ${escapeHtml(log.technicianName)}</p>
<p><strong>Következő ellenőrzés:</strong> ${formatDate(log.nextInspectionDate)}</p>
<p><strong>Megjegyzés:</strong> ${escapeHtml(log.notes || "Nincs megjegyzés.")}</p>
<div class="file-list">${fileMarkup}</div>
</article>
`;
})
.join("");
}
function renderError(message) {
elements.entriesList.innerHTML = `
<article class="entry-card empty-card">
<h3>Hiba</h3>
<p>${escapeHtml(message)}</p>
</article>
`;
}
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("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
}