|
|
|
@ -20,20 +20,66 @@ |
|
|
|
|
|
|
|
<body> |
|
|
|
<!-- Navigation --> |
|
|
|
<th:block th:replace="~{fragments/navbar :: navbar(curPage=${curPage}, isAdmin=${isAdmin})}"></th:block> |
|
|
|
<th:block th:replace="~{fragments/navbar :: navbar(curPage=${curPage}, isAdmin=${isAdmin}, isLoggedIn=${isLoggedIn})}"></th:block> |
|
|
|
|
|
|
|
<div class="container"> |
|
|
|
<div class="form-container"> |
|
|
|
<!-- Back Button to Home (State 1) --> |
|
|
|
<div class="header-row" id="category-back-row"> |
|
|
|
<div class="header-row" id="category-back-row" style="justify-content: space-between; width: 100%;"> |
|
|
|
<button type="button" class="back-button" onclick="window.history.back()">← Back</button> |
|
|
|
|
|
|
|
<!-- CREDENTIALS SECTION (Top Right) --> |
|
|
|
<div th:if="${isLoggedIn && credentialUrls != null && !credentialUrls.isEmpty()}" class="dropdown credentials-dropdown"> |
|
|
|
<button class="back-button dropdown-toggle" type="button" id="credentialsMenuButton" data-bs-toggle="dropdown" aria-expanded="false" style="margin-left: auto;"> |
|
|
|
<i class="fa-solid fa-key" style="margin-right: 5px;"></i> View Credentials |
|
|
|
</button> |
|
|
|
<ul class="dropdown-menu dropdown-menu-end p-3" aria-labelledby="credentialsMenuButton" style="min-width: 320px; max-height: 400px; overflow-y: auto; box-shadow: 0 10px 25px rgba(0,0,0,0.15); border-radius: 8px; border: 1px solid rgba(0,0,0,0.1);"> |
|
|
|
<h6 class="dropdown-header text-center" style="font-weight: bold; color: #003366; font-size: 1rem; border-bottom: 2px solid #005f73; padding-bottom: 8px; margin-bottom: 10px; text-decoration: none;">Page Credentials</h6> |
|
|
|
<li th:each="cUrl : ${credentialUrls}" class="credential-item" th:data-url-name="${cUrl.urlDataName}" style="margin-bottom: 12px; border-bottom: 1px solid #eee; padding-bottom: 8px;"> |
|
|
|
<div style="font-weight: bold; color: #005f73; font-size: 0.95rem; margin-bottom: 4px;" th:text="${cUrl.urlDataName}">URL Name</div> |
|
|
|
<div style="font-size: 0.85rem; color: #555; margin-bottom: 2px;"> |
|
|
|
<strong>Username:</strong> <span th:text="${cUrl.urlDataUsrName}" style="font-family: monospace; font-size: 0.9rem;">user</span> |
|
|
|
</div> |
|
|
|
<div style="font-size: 0.85rem; color: #555; display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div> |
|
|
|
<strong>Password:</strong> |
|
|
|
<span th:id="'cat-pass-' + ${cUrl.urlDataId}" th:text="${'*'.repeat(cUrl.urlDataUsrPass?.length() ?: 0)}" style="font-family: monospace; font-size: 0.9rem;">pass</span> |
|
|
|
</div> |
|
|
|
<i class="fa-solid fa-eye toggle-cat-pass" th:attr="data-target='cat-pass-' + ${cUrl.urlDataId}, data-pass=${cUrl.urlDataUsrPass}" style="cursor: pointer; margin-left: 8px; color: #005f73; font-size: 1rem;"></i> |
|
|
|
</div> |
|
|
|
</li> |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Back Button to Subcategories (State 2) --> |
|
|
|
<div class="header-row" id="subcat-back-row" style="display: none;"> |
|
|
|
<div class="header-row" id="subcat-back-row" style="display: none; justify-content: space-between; width: 100%;"> |
|
|
|
<button type="button" id="back-to-subcats-btn" class="back-button"> |
|
|
|
← Back |
|
|
|
</button> |
|
|
|
|
|
|
|
<!-- CREDENTIALS SECTION (Top Right) --> |
|
|
|
<div th:if="${isLoggedIn && credentialUrls != null && !credentialUrls.isEmpty()}" class="dropdown credentials-dropdown"> |
|
|
|
<button class="back-button dropdown-toggle" type="button" id="credentialsMenuButtonSub" data-bs-toggle="dropdown" aria-expanded="false" style="margin-left: auto;"> |
|
|
|
<i class="fa-solid fa-key" style="margin-right: 5px;"></i> View Credentials |
|
|
|
</button> |
|
|
|
<ul class="dropdown-menu dropdown-menu-end p-3" aria-labelledby="credentialsMenuButtonSub" style="min-width: 320px; max-height: 400px; overflow-y: auto; box-shadow: 0 10px 25px rgba(0,0,0,0.15); border-radius: 8px; border: 1px solid rgba(0,0,0,0.1);"> |
|
|
|
<h6 class="dropdown-header text-center" style="font-weight: bold; color: #003366; font-size: 1rem; border-bottom: 2px solid #005f73; padding-bottom: 8px; margin-bottom: 10px; text-decoration: none;">Page Credentials</h6> |
|
|
|
<li th:each="cUrl : ${credentialUrls}" class="credential-item" th:data-url-name="${cUrl.urlDataName}" style="margin-bottom: 12px; border-bottom: 1px solid #eee; padding-bottom: 8px;"> |
|
|
|
<div style="font-weight: bold; color: #005f73; font-size: 0.95rem; margin-bottom: 4px;" th:text="${cUrl.urlDataName}">URL Name</div> |
|
|
|
<div style="font-size: 0.85rem; color: #555; margin-bottom: 2px;"> |
|
|
|
<strong>Username:</strong> <span th:text="${cUrl.urlDataUsrName}" style="font-family: monospace; font-size: 0.9rem;">user</span> |
|
|
|
</div> |
|
|
|
<div style="font-size: 0.85rem; color: #555; display: flex; align-items: center; justify-content: space-between;"> |
|
|
|
<div> |
|
|
|
<strong>Password:</strong> |
|
|
|
<span th:id="'cat-pass-sub-' + ${cUrl.urlDataId}" th:text="${'*'.repeat(cUrl.urlDataUsrPass?.length() ?: 0)}" style="font-family: monospace; font-size: 0.9rem;">pass</span> |
|
|
|
</div> |
|
|
|
<i class="fa-solid fa-eye toggle-cat-pass" th:attr="data-target='cat-pass-sub-' + ${cUrl.urlDataId}, data-pass=${cUrl.urlDataUsrPass}" style="cursor: pointer; margin-left: 8px; color: #005f73; font-size: 1rem;"></i> |
|
|
|
</div> |
|
|
|
</li> |
|
|
|
</ul> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Heading + Search Bar on same line --> |
|
|
|
@ -65,11 +111,11 @@ |
|
|
|
<th:block th:each="entry : ${groupedUrls}" th:if="${entry.key == '-'}"> |
|
|
|
<button type="button" th:each="url : ${entry.value}" th:attr="data-link = ${url.urlDataLink.startsWith('/urls')} |
|
|
|
? @{${url.urlDataLink}} |
|
|
|
: ${url.urlDataLink}" th:onclick="${url.urlDataLink.startsWith('/urls')} |
|
|
|
: ${url.urlDataLink}, data-url-name = ${url.urlDataName}" th:onclick="${url.urlDataLink.startsWith('/urls')} |
|
|
|
? 'window.location.href=this.dataset.link' |
|
|
|
: 'window.open(this.dataset.link, \'_blank\')'" class="custom-button url-service-btn direct-url-btn" |
|
|
|
data-subcat="-"> |
|
|
|
<span class="url-num-badge">[[${url.urlDataNum}]]</span> |
|
|
|
data-subcat="-" th:data-url-name="${url.urlDataName}"> |
|
|
|
<!-- <span class="url-num-badge">[[${url.urlDataNum}]]</span> --> |
|
|
|
[[${url.urlDataName}]] |
|
|
|
</button> |
|
|
|
</th:block> |
|
|
|
@ -78,8 +124,8 @@ |
|
|
|
<th:block th:each="entry : ${groupedUrls}" th:if="${entry.key != '-'}"> |
|
|
|
<button type="button" class="custom-button subcat-folder-btn" th:data-target-subcat="${entry.key}" |
|
|
|
th:id="'subcat-folder-' + ${#strings.replace(entry.key, ' ', '_')}"> |
|
|
|
<span class="url-num-badge" |
|
|
|
th:text="${(entry.value != null && !entry.value.isEmpty() && entry.value.get(0).urlDataNum != null) ? entry.value.get(0).urlDataNum.substring(0, 1) : ''}">1</span> |
|
|
|
<!-- <span |
|
|
|
th:text="${(entry.value != null && !entry.value.isEmpty() && entry.value.get(0).urlDataNum != null) ? entry.value.get(0).urlDataNum.substring(0, 1) : ''}"></span> --> |
|
|
|
<span class="subcat-title-text">[[${entry.key}]]</span> |
|
|
|
</button> |
|
|
|
</th:block> |
|
|
|
@ -88,11 +134,11 @@ |
|
|
|
<th:block th:each="entry : ${groupedUrls}" th:if="${entry.key != '-'}"> |
|
|
|
<button type="button" th:each="url : ${entry.value}" th:attr="data-link = ${url.urlDataLink.startsWith('/urls')} |
|
|
|
? @{${url.urlDataLink}} |
|
|
|
: ${url.urlDataLink}" th:onclick="${url.urlDataLink.startsWith('/urls')} |
|
|
|
: ${url.urlDataLink}, data-url-name = ${url.urlDataName}" th:onclick="${url.urlDataLink.startsWith('/urls')} |
|
|
|
? 'window.location.href=this.dataset.link' |
|
|
|
: 'window.open(this.dataset.link, \'_blank\')'" class="custom-button url-service-btn subcat-url-btn" |
|
|
|
th:data-subcat="${entry.key}" style="display: none;"> |
|
|
|
<span class="url-num-badge">[[${url.urlDataNum}]]</span> |
|
|
|
th:data-subcat="${entry.key}" style="display: none;" th:data-url-name="${url.urlDataName}"> |
|
|
|
<!-- <span class="url-num-badge">[[${url.urlDataNum}]]</span> --> |
|
|
|
[[${url.urlDataName}]] |
|
|
|
</button> |
|
|
|
</th:block> |
|
|
|
@ -115,6 +161,63 @@ |
|
|
|
<!-- Custom JS --> |
|
|
|
<script th:inline="javascript"> |
|
|
|
const contextPath = /*[[@{/}]]*/ ''; |
|
|
|
|
|
|
|
// Function to show/hide credentials in the dropdown based on currently visible buttons |
|
|
|
window.updateDropdownCredentials = function() { |
|
|
|
const visibleUrlNames = new Set(); |
|
|
|
document.querySelectorAll(".url-service-btn").forEach(btn => { |
|
|
|
if (btn.style.display !== "none") { |
|
|
|
const name = btn.getAttribute("data-url-name"); |
|
|
|
if (name) visibleUrlNames.add(name.toLowerCase()); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
let visibleCount = 0; |
|
|
|
document.querySelectorAll(".credential-item").forEach(item => { |
|
|
|
const name = item.getAttribute("data-url-name"); |
|
|
|
if (name && visibleUrlNames.has(name.toLowerCase())) { |
|
|
|
item.style.display = "block"; |
|
|
|
visibleCount++; |
|
|
|
} else { |
|
|
|
item.style.display = "none"; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// Hide the credentials dropdown entirely if no credentials match the currently shown services |
|
|
|
document.querySelectorAll(".credentials-dropdown").forEach(dd => { |
|
|
|
if (visibleCount > 0) { |
|
|
|
dd.style.setProperty("display", "block", "important"); |
|
|
|
} else { |
|
|
|
dd.style.setProperty("display", "none", "important"); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
document.addEventListener("DOMContentLoaded", () => { |
|
|
|
// Handle clicks inside the credentials dropdown menu |
|
|
|
document.querySelectorAll('.dropdown-menu').forEach(menu => { |
|
|
|
menu.addEventListener('click', function (e) { |
|
|
|
// Prevent closing when clicking toggle-cat-pass or any other elements inside |
|
|
|
e.stopPropagation(); |
|
|
|
|
|
|
|
if (e.target && e.target.classList.contains("toggle-cat-pass")) { |
|
|
|
e.preventDefault(); |
|
|
|
const icon = e.target; |
|
|
|
const targetId = icon.getAttribute("data-target"); |
|
|
|
const passwordElement = document.getElementById(targetId); |
|
|
|
const originalPassword = icon.getAttribute("data-pass"); |
|
|
|
|
|
|
|
if (passwordElement.textContent.includes("*")) { |
|
|
|
passwordElement.textContent = originalPassword; |
|
|
|
icon.classList.replace("fa-eye", "fa-eye-slash"); |
|
|
|
} else { |
|
|
|
passwordElement.textContent = "*".repeat(originalPassword.length); |
|
|
|
icon.classList.replace("fa-eye-slash", "fa-eye"); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
</script> |
|
|
|
<script th:src="@{/assets/js/home-script.js}" type="text/javascript" defer></script> |
|
|
|
</body> |
|
|
|
|