User Panel added API Docs link

This commit is contained in:
Ryan
2025-04-17 06:45:00 -04:00
committed by GitHub
parent cd9d7eb0ba
commit 3b58123584
3 changed files with 72 additions and 63 deletions

View File

@@ -5,6 +5,7 @@
- Generate OpenAPI spec and API HTML docs - Generate OpenAPI spec and API HTML docs
- Fully autogenerated OpenAPI spec (`openapi.json`) and interactive HTML docs (`api.html`) powered by Redoc. - Fully autogenerated OpenAPI spec (`openapi.json`) and interactive HTML docs (`api.html`) powered by Redoc.
- .gitattributes added to mark (`openapi.json`) & (`api.html`) as documentation. - .gitattributes added to mark (`openapi.json`) & (`api.html`) as documentation.
- User Panel added API Docs link.
--- ---

View File

@@ -166,105 +166,112 @@ export function openUserPanel() {
border-radius: 8px; border-radius: 8px;
position: fixed; position: fixed;
overflow-y: auto; overflow-y: auto;
max-height: 350px !important; max-height: 400px !important;
border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"}; border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"};
transform: none; transform: none;
transition: none; transition: none;
`; `;
// Retrieve the language setting from local storage, default to English ("en")
const savedLanguage = localStorage.getItem("language") || "en"; const savedLanguage = localStorage.getItem("language") || "en";
if (!userPanelModal) { if (!userPanelModal) {
userPanelModal = document.createElement("div"); userPanelModal = document.createElement("div");
userPanelModal.id = "userPanelModal"; userPanelModal.id = "userPanelModal";
userPanelModal.style.cssText = ` userPanelModal.style.cssText = `
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: ${overlayBackground}; background-color: ${overlayBackground};
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
z-index: 3000; z-index: 3000;
`; `;
userPanelModal.innerHTML = ` userPanelModal.innerHTML = `
<div class="modal-content user-panel-content" style="${modalContentStyles}"> <div class="modal-content user-panel-content" style="${modalContentStyles}">
<span id="closeUserPanel" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">&times;</span> <span id="closeUserPanel" style="position: absolute; top: 10px; right: 10px; cursor: pointer; font-size: 24px;">&times;</span>
<h3>${t("user_panel")} (${username})</h3> <h3>${t("user_panel")} (${username})</h3>
<button type="button" id="openChangePasswordModalBtn" class="btn btn-primary" style="margin-bottom: 15px;">${t("change_password")}</button>
<fieldset style="margin-bottom: 15px;"> <button type="button" id="openChangePasswordModalBtn" class="btn btn-primary" style="margin-bottom: 15px;">
<legend>${t("totp_settings")}</legend> ${t("change_password")}
<div class="form-group"> </button>
<label for="userTOTPEnabled">${t("enable_totp")}:</label>
<input type="checkbox" id="userTOTPEnabled" style="vertical-align: middle;" /> <fieldset style="margin-bottom: 15px;">
</div> <legend>${t("totp_settings")}</legend>
</fieldset> <div class="form-group">
<fieldset style="margin-bottom: 15px;"> <label for="userTOTPEnabled">${t("enable_totp")}:</label>
<legend>${t("language")}</legend> <input type="checkbox" id="userTOTPEnabled" style="vertical-align: middle;" />
<div class="form-group"> </div>
<label for="languageSelector">${t("select_language")}:</label> </fieldset>
<select id="languageSelector">
<option value="en">${t("english")}</option> <fieldset style="margin-bottom: 15px;">
<option value="es">${t("spanish")}</option> <legend>${t("language")}</legend>
<option value="fr">${t("french")}</option> <div class="form-group">
<option value="de">${t("german")}</option> <label for="languageSelector">${t("select_language")}:</label>
</select> <select id="languageSelector">
</div> <option value="en">${t("english")}</option>
</fieldset> <option value="es">${t("spanish")}</option>
<option value="fr">${t("french")}</option>
<option value="de">${t("german")}</option>
</select>
</div>
</fieldset>
<!-- New API Docs link -->
<div style="margin-bottom: 15px;">
<a href="api.html" target="_blank" class="btn btn-secondary">
${t("api_docs") || "API Docs"}
</a>
</div> </div>
`; </div>
`;
document.body.appendChild(userPanelModal); document.body.appendChild(userPanelModal);
// Close button handler
// Handlers…
document.getElementById("closeUserPanel").addEventListener("click", () => { document.getElementById("closeUserPanel").addEventListener("click", () => {
userPanelModal.style.display = "none"; userPanelModal.style.display = "none";
}); });
// Change Password button
document.getElementById("openChangePasswordModalBtn").addEventListener("click", () => { document.getElementById("openChangePasswordModalBtn").addEventListener("click", () => {
document.getElementById("changePasswordModal").style.display = "block"; document.getElementById("changePasswordModal").style.display = "block";
}); });
// TOTP checkbox behavior
// TOTP checkbox
const totpCheckbox = document.getElementById("userTOTPEnabled"); const totpCheckbox = document.getElementById("userTOTPEnabled");
totpCheckbox.checked = localStorage.getItem("userTOTPEnabled") === "true"; totpCheckbox.checked = localStorage.getItem("userTOTPEnabled") === "true";
totpCheckbox.addEventListener("change", function () { totpCheckbox.addEventListener("change", function () {
localStorage.setItem("userTOTPEnabled", this.checked ? "true" : "false"); localStorage.setItem("userTOTPEnabled", this.checked ? "true" : "false");
const enabled = this.checked;
fetch("api/updateUserPanel.php", { fetch("api/updateUserPanel.php", {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: { headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken },
"Content-Type": "application/json", body: JSON.stringify({ totp_enabled: this.checked })
"X-CSRF-Token": window.csrfToken
},
body: JSON.stringify({ totp_enabled: enabled })
}) })
.then(r => r.json()) .then(r => r.json())
.then(result => { .then(result => {
if (!result.success) { if (!result.success) showToast(t("error_updating_totp_setting") + ": " + result.error);
showToast(t("error_updating_totp_setting") + ": " + result.error); else if (this.checked) openTOTPModal();
} else if (enabled) {
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"); const languageSelector = document.getElementById("languageSelector");
languageSelector.value = savedLanguage; languageSelector.value = savedLanguage;
languageSelector.addEventListener("change", function () { languageSelector.addEventListener("change", function () {
const selectedLanguage = this.value; localStorage.setItem("language", this.value);
localStorage.setItem("language", selectedLanguage); setLocale(this.value);
setLocale(selectedLanguage);
applyTranslations(); applyTranslations();
}); });
} else { } else {
// If the modal already exists, update its colors // Update colors if already exists
userPanelModal.style.backgroundColor = overlayBackground; userPanelModal.style.backgroundColor = overlayBackground;
const modalContent = userPanelModal.querySelector(".modal-content"); const modalContent = userPanelModal.querySelector(".modal-content");
modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff";
modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000";
modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc"; modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc";
} }
userPanelModal.style.display = "flex"; userPanelModal.style.display = "flex";
} }
@@ -549,7 +556,7 @@ function showCustomConfirmModal(message) {
noBtn.removeEventListener("click", onNo); noBtn.removeEventListener("click", onNo);
modal.style.display = "none"; modal.style.display = "none";
} }
yesBtn.addEventListener("click", onYes); yesBtn.addEventListener("click", onYes);
noBtn.addEventListener("click", onNo); noBtn.addEventListener("click", onNo);
}); });
@@ -688,7 +695,7 @@ export function openAdminPanel() {
openUserPermissionsModal(); openUserPermissionsModal();
}); });
document.getElementById("saveAdminSettings").addEventListener("click", () => { document.getElementById("saveAdminSettings").addEventListener("click", () => {
const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
@@ -707,7 +714,7 @@ export function openAdminPanel() {
return; return;
} }
const newHeaderTitle = document.getElementById("headerTitle").value.trim(); const newHeaderTitle = document.getElementById("headerTitle").value.trim();
const newOIDCConfig = { const newOIDCConfig = {
providerUrl: document.getElementById("oidcProviderUrl").value.trim(), providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
clientId: document.getElementById("oidcClientId").value.trim(), clientId: document.getElementById("oidcClientId").value.trim(),
@@ -739,7 +746,7 @@ export function openAdminPanel() {
captureInitialAdminConfig(); captureInitialAdminConfig();
closeAdminPanel(); closeAdminPanel();
loadAdminConfigFunc(); loadAdminConfigFunc();
} else { } else {
showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error"))); 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("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
// Capture initial state after the modal loads. // Capture initial state after the modal loads.
captureInitialAdminConfig(); captureInitialAdminConfig();
} else { } else {

View File

@@ -237,7 +237,8 @@ const translations = {
"ok": "OK", "ok": "OK",
"show": "Show", "show": "Show",
"items_per_page": "items per page", "items_per_page": "items per page",
"columns":"Columns" "columns":"Columns",
"api_docs": "API Docs"
}, },
es: { es: {
"please_log_in_to_continue": "Por favor, inicie sesión para continuar.", "please_log_in_to_continue": "Por favor, inicie sesión para continuar.",