diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b6076b..3232fa2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
- Generate OpenAPI spec and API HTML docs
- Fully auto‑generated OpenAPI spec (`openapi.json`) and interactive HTML docs (`api.html`) powered by Redoc.
- .gitattributes added to mark (`openapi.json`) & (`api.html`) as documentation.
+- User Panel added API Docs link.
---
diff --git a/public/js/authModals.js b/public/js/authModals.js
index 8e2b774..e529e1a 100644
--- a/public/js/authModals.js
+++ b/public/js/authModals.js
@@ -166,105 +166,112 @@ export function openUserPanel() {
border-radius: 8px;
position: fixed;
overflow-y: auto;
- max-height: 350px !important;
+ max-height: 400px !important;
border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"};
transform: none;
transition: none;
`;
- // Retrieve the language setting from local storage, default to English ("en")
const savedLanguage = localStorage.getItem("language") || "en";
+
if (!userPanelModal) {
userPanelModal = document.createElement("div");
userPanelModal.id = "userPanelModal";
userPanelModal.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- background-color: ${overlayBackground};
- display: flex;
- justify-content: center;
- align-items: center;
- z-index: 3000;
- `;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background-color: ${overlayBackground};
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 3000;
+ `;
userPanelModal.innerHTML = `
-
-
×
-
${t("user_panel")} (${username})
-
-
-
+
+
×
+
${t("user_panel")} (${username})
+
+
+
+
+
+
+
+
+
- `;
+
+ `;
document.body.appendChild(userPanelModal);
- // Close button handler
+
+ // Handlers…
document.getElementById("closeUserPanel").addEventListener("click", () => {
userPanelModal.style.display = "none";
});
- // Change Password button
document.getElementById("openChangePasswordModalBtn").addEventListener("click", () => {
document.getElementById("changePasswordModal").style.display = "block";
});
- // TOTP checkbox behavior
+
+ // TOTP checkbox
const totpCheckbox = document.getElementById("userTOTPEnabled");
totpCheckbox.checked = localStorage.getItem("userTOTPEnabled") === "true";
totpCheckbox.addEventListener("change", function () {
localStorage.setItem("userTOTPEnabled", this.checked ? "true" : "false");
- const enabled = this.checked;
fetch("api/updateUserPanel.php", {
method: "POST",
credentials: "include",
- headers: {
- "Content-Type": "application/json",
- "X-CSRF-Token": window.csrfToken
- },
- body: JSON.stringify({ totp_enabled: enabled })
+ headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken },
+ body: JSON.stringify({ totp_enabled: this.checked })
})
.then(r => r.json())
.then(result => {
- if (!result.success) {
- showToast(t("error_updating_totp_setting") + ": " + result.error);
- } else if (enabled) {
- openTOTPModal();
- }
+ if (!result.success) showToast(t("error_updating_totp_setting") + ": " + result.error);
+ else if (this.checked) openTOTPModal();
})
- .catch(() => { showToast(t("error_updating_totp_setting")); });
+ .catch(() => showToast(t("error_updating_totp_setting")));
});
- // Language dropdown initialization
+
+ // Language selector
const languageSelector = document.getElementById("languageSelector");
languageSelector.value = savedLanguage;
languageSelector.addEventListener("change", function () {
- const selectedLanguage = this.value;
- localStorage.setItem("language", selectedLanguage);
- setLocale(selectedLanguage);
+ localStorage.setItem("language", this.value);
+ setLocale(this.value);
applyTranslations();
});
} else {
- // If the modal already exists, update its colors
+ // Update colors if already exists
userPanelModal.style.backgroundColor = overlayBackground;
const modalContent = userPanelModal.querySelector(".modal-content");
modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff";
modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000";
modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc";
}
+
userPanelModal.style.display = "flex";
}
@@ -549,7 +556,7 @@ function showCustomConfirmModal(message) {
noBtn.removeEventListener("click", onNo);
modal.style.display = "none";
}
-
+
yesBtn.addEventListener("click", onYes);
noBtn.addEventListener("click", onNo);
});
@@ -688,7 +695,7 @@ export function openAdminPanel() {
openUserPermissionsModal();
});
document.getElementById("saveAdminSettings").addEventListener("click", () => {
-
+
const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
@@ -707,7 +714,7 @@ export function openAdminPanel() {
return;
}
const newHeaderTitle = document.getElementById("headerTitle").value.trim();
-
+
const newOIDCConfig = {
providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
clientId: document.getElementById("oidcClientId").value.trim(),
@@ -739,7 +746,7 @@ export function openAdminPanel() {
captureInitialAdminConfig();
closeAdminPanel();
loadAdminConfigFunc();
-
+
} else {
showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error")));
}
@@ -764,7 +771,7 @@ export function openAdminPanel() {
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
-
+
// Capture initial state after the modal loads.
captureInitialAdminConfig();
} else {
diff --git a/public/js/i18n.js b/public/js/i18n.js
index d178325..66cb7e7 100644
--- a/public/js/i18n.js
+++ b/public/js/i18n.js
@@ -237,7 +237,8 @@ const translations = {
"ok": "OK",
"show": "Show",
"items_per_page": "items per page",
- "columns":"Columns"
+ "columns":"Columns",
+ "api_docs": "API Docs"
},
es: {
"please_log_in_to_continue": "Por favor, inicie sesión para continuar.",