|
31 | 31 | #backup-list-modal-overlay.active #backup-list-modal-content { |
32 | 32 | transform: scale(1); |
33 | 33 | } |
| 34 | +
|
| 35 | + #system-logs-modal-overlay.active { |
| 36 | + visibility: visible; |
| 37 | + opacity: 1; |
| 38 | + } |
| 39 | +
|
| 40 | + #system-logs-modal-overlay.active #system-logs-modal-content { |
| 41 | + transform: scale(1); |
| 42 | + } |
34 | 43 | </style> |
35 | 44 | </head> |
36 | 45 |
|
|
48 | 57 | </header> |
49 | 58 |
|
50 | 59 | <div class="bg-white rounded-xl shadow-lg p-6 mb-6"> |
51 | | - <div class="flex items-center mb-4"> |
52 | | - <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-500 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> |
53 | | - <path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" /> |
54 | | - </svg> |
55 | | - <h2 class="font-bold text-xl text-gray-800" id="StatsTitle" data-translate="AdminSettings.Stats.Title"></h2> |
| 60 | + <div class="flex flex-col gap-3 mb-4 sm:flex-row sm:items-center sm:justify-between"> |
| 61 | + <div class="flex items-center"> |
| 62 | + <svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-500 mr-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> |
| 63 | + <path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" /> |
| 64 | + </svg> |
| 65 | + <h2 class="font-bold text-xl text-gray-800" id="StatsTitle" data-translate="AdminSettings.Stats.Title"></h2> |
| 66 | + </div> |
| 67 | + <button id="show-system-logs-btn" onclick="showSystemLogsModal()" class="inline-flex items-center justify-center gap-2 bg-gray-700 text-white font-bold py-2 px-4 rounded-lg hover:bg-gray-800 transition"> |
| 68 | + <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> |
| 69 | + <path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6M9 8h6m2 13H7a2 2 0 01-2-2V5a2 2 0 012-2h7l5 5v11a2 2 0 01-2 2z" /> |
| 70 | + </svg> |
| 71 | + <span data-translate="AdminSettings.Logs.Button"></span> |
| 72 | + </button> |
56 | 73 | </div> |
57 | 74 |
|
58 | 75 | <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-6 gap-y-4 text-sm"> |
|
257 | 274 | </div> |
258 | 275 | </div> |
259 | 276 |
|
| 277 | + <div id="system-logs-modal-overlay" class="fixed inset-0 bg-black bg-opacity-60 flex items-center justify-center p-4 z-50 invisible opacity-0 transition-opacity duration-300"> |
| 278 | + <div id="system-logs-modal-content" class="bg-white rounded-xl shadow-2xl p-6 max-w-5xl w-full relative transform scale-95 transition-transform duration-300"> |
| 279 | + <button onclick="closeSystemLogsModal()" class="absolute top-3 right-4 text-gray-500 hover:text-gray-800 text-3xl font-bold">×</button> |
| 280 | + <h2 class="text-2xl font-bold text-gray-800 mb-4 pr-8" data-translate="AdminSettings.Logs.ModalTitle"></h2> |
| 281 | + |
| 282 | + <div class="flex flex-wrap items-center gap-3 mb-4" id="system-log-filter-container"> |
| 283 | + <span class="text-sm font-semibold text-gray-700" data-translate="AdminSettings.Logs.Filters"></span> |
| 284 | + <label class="inline-flex items-center gap-2 text-sm text-gray-700"> |
| 285 | + <input type="checkbox" class="system-log-filter rounded border-gray-300" value="error" checked /> |
| 286 | + <span data-translate="AdminSettings.Logs.Levels.Error"></span> |
| 287 | + </label> |
| 288 | + <label class="inline-flex items-center gap-2 text-sm text-gray-700"> |
| 289 | + <input type="checkbox" class="system-log-filter rounded border-gray-300" value="warning" checked /> |
| 290 | + <span data-translate="AdminSettings.Logs.Levels.Warning"></span> |
| 291 | + </label> |
| 292 | + <label class="inline-flex items-center gap-2 text-sm text-gray-700"> |
| 293 | + <input type="checkbox" class="system-log-filter rounded border-gray-300" value="info" checked /> |
| 294 | + <span data-translate="AdminSettings.Logs.Levels.Info"></span> |
| 295 | + </label> |
| 296 | + <label class="inline-flex items-center gap-2 text-sm text-gray-700"> |
| 297 | + <input type="checkbox" class="system-log-filter rounded border-gray-300" value="debug" checked /> |
| 298 | + <span data-translate="AdminSettings.Logs.Levels.Debug"></span> |
| 299 | + </label> |
| 300 | + <label class="inline-flex items-center gap-2 text-sm text-gray-700"> |
| 301 | + <input type="checkbox" class="system-log-filter rounded border-gray-300" value="system" checked /> |
| 302 | + <span data-translate="AdminSettings.Logs.Levels.System"></span> |
| 303 | + </label> |
| 304 | + </div> |
| 305 | + |
| 306 | + <div id="system-logs-container" class="max-h-[65vh] overflow-y-auto rounded-lg bg-gray-950 p-3 font-mono text-xs text-gray-100"> |
| 307 | + <p class="text-gray-400">...</p> |
| 308 | + </div> |
| 309 | + </div> |
| 310 | + </div> |
| 311 | + |
260 | 312 | <div id="favicon-cropper-modal" class="cropper-modal-container"> |
261 | 313 | <div class="cropper-modal-content"> |
262 | 314 | <h2 class="text-xl font-bold mb-4" data-translate="AdminSettings.Manifest.AdjustFavicon"></h2> |
|
295 | 347 | let croppedFaviconBlob = null; |
296 | 348 | let lowFundsCropper; |
297 | 349 | let croppedLowFundsBlob = null; |
| 350 | + let systemLogs = []; |
| 351 | +
|
| 352 | + const logLevelClasses = { |
| 353 | + error: "text-red-300", |
| 354 | + warning: "text-yellow-300", |
| 355 | + info: "text-green-300", |
| 356 | + debug: "text-fuchsia-300", |
| 357 | + system: "text-cyan-300", |
| 358 | + }; |
| 359 | +
|
298 | 360 | function formatBytes(bytes, decimals = 2) { |
299 | 361 | if (!+bytes) return "0 Bytes"; |
300 | 362 | const k = 1024; |
|
321 | 383 | return result || "..."; |
322 | 384 | } |
323 | 385 |
|
| 386 | + function getSelectedLogLevels() { |
| 387 | + return new Set( |
| 388 | + Array.from(document.querySelectorAll(".system-log-filter:checked")) |
| 389 | + .map((filter) => filter.value) |
| 390 | + ); |
| 391 | + } |
| 392 | +
|
| 393 | + function renderSystemLogs() { |
| 394 | + const listContainer = document.getElementById("system-logs-container"); |
| 395 | + const selectedLevels = getSelectedLogLevels(); |
| 396 | + const visibleLogs = systemLogs.filter((log) => selectedLevels.has((log.level || "").toLowerCase())); |
| 397 | +
|
| 398 | + listContainer.innerHTML = ""; |
| 399 | +
|
| 400 | + if (!visibleLogs.length) { |
| 401 | + const emptyMessage = document.createElement("p"); |
| 402 | + emptyMessage.className = "text-gray-400"; |
| 403 | + emptyMessage.textContent = t("AdminSettings.Logs.Empty"); |
| 404 | + listContainer.appendChild(emptyMessage); |
| 405 | + return; |
| 406 | + } |
| 407 | +
|
| 408 | + visibleLogs.forEach((log) => { |
| 409 | + const level = (log.level || "info").toLowerCase(); |
| 410 | + const entry = document.createElement("div"); |
| 411 | + entry.className = "grid gap-2 border-b border-gray-800 py-1 last:border-b-0 md:grid-cols-[9rem_5rem_1fr]"; |
| 412 | +
|
| 413 | + const timestamp = document.createElement("span"); |
| 414 | + timestamp.className = "text-gray-400"; |
| 415 | + timestamp.textContent = log.timestamp || ""; |
| 416 | +
|
| 417 | + const levelElement = document.createElement("span"); |
| 418 | + levelElement.className = `font-bold uppercase ${logLevelClasses[level] || "text-gray-300"}`; |
| 419 | + levelElement.textContent = level; |
| 420 | +
|
| 421 | + const text = document.createElement("span"); |
| 422 | + text.className = "whitespace-pre-wrap break-words text-gray-100"; |
| 423 | + text.textContent = log.text || ""; |
| 424 | +
|
| 425 | + entry.appendChild(timestamp); |
| 426 | + entry.appendChild(levelElement); |
| 427 | + entry.appendChild(text); |
| 428 | + listContainer.appendChild(entry); |
| 429 | + }); |
| 430 | +
|
| 431 | + listContainer.scrollTop = listContainer.scrollHeight; |
| 432 | + } |
| 433 | +
|
| 434 | + async function loadSystemLogs() { |
| 435 | + const listContainer = document.getElementById("system-logs-container"); |
| 436 | + listContainer.innerHTML = `<p class="text-gray-400">${t("AdminSettings.Logs.Loading")}</p>`; |
| 437 | +
|
| 438 | + try { |
| 439 | + const response = await apiFetch("/api/v1/settings/logs"); |
| 440 | + if (!response.ok) throw new Error("Network response was not ok"); |
| 441 | + const data = await response.json(); |
| 442 | + systemLogs = data.logs || []; |
| 443 | + renderSystemLogs(); |
| 444 | + } catch (error) { |
| 445 | + console.error("Error fetching system logs:", error); |
| 446 | + listContainer.innerHTML = `<p class="text-red-300">${t("AdminSettings.Logs.Error")}</p>`; |
| 447 | + } |
| 448 | + } |
| 449 | +
|
| 450 | + function showSystemLogsModal() { |
| 451 | + const modalOverlay = document.getElementById("system-logs-modal-overlay"); |
| 452 | + modalOverlay.classList.add("active"); |
| 453 | + loadSystemLogs(); |
| 454 | + } |
| 455 | +
|
| 456 | + function closeSystemLogsModal() { |
| 457 | + const modalOverlay = document.getElementById("system-logs-modal-overlay"); |
| 458 | + modalOverlay.classList.remove("active"); |
| 459 | + } |
| 460 | +
|
324 | 461 | async function toggleSetting(settingKey) { |
325 | 462 | try { |
326 | 463 | const response = await apiFetch("/api/v1/settings/toggle", { |
|
870 | 1007 | document.getElementById("manifest-settings-form").addEventListener("submit", handleManifestSubmit); |
871 | 1008 | document.getElementById("low-funds-image").addEventListener("change", handleLowFundsImageSelection); |
872 | 1009 | document.getElementById("low-funds-settings-form").addEventListener("submit", handleLowFundsSubmit); |
| 1010 | + document.querySelectorAll(".system-log-filter").forEach((filter) => { |
| 1011 | + filter.addEventListener("change", renderSystemLogs); |
| 1012 | + }); |
873 | 1013 | document.getElementById("crop-favicon-button").addEventListener("click", () => { |
874 | 1014 | if (!faviconCropper) return; |
875 | 1015 |
|
|
0 commit comments