mirror of
https://github.com/hyzendust/hyzendust.github.io.git
synced 2026-06-30 23:12:16 +02:00
Compare commits
6 Commits
6b93a6b3bf
...
f974308b74
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f974308b74 | ||
|
|
25d7f545cf | ||
|
|
b4ee52377e | ||
|
|
50b6d25917 | ||
|
|
bae7bd8325 | ||
|
|
e79fa2eb5f |
@@ -2,6 +2,7 @@
|
|||||||
draft = false
|
draft = false
|
||||||
title = 'Email'
|
title = 'Email'
|
||||||
weight = 1
|
weight = 1
|
||||||
|
registration-needed = true
|
||||||
+++
|
+++
|
||||||
|
|
||||||
<div id="email-loggedin" style="display:none">
|
<div id="email-loggedin" style="display:none">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
draft = false
|
draft = false
|
||||||
title = 'File Share'
|
title = 'File Share'
|
||||||
weight = 3
|
weight = 3
|
||||||
|
registration-needed = false
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Visit <a href="https://share.freedoms4.org" target="_blank" rel="noopener noreferrer">
|
Visit <a href="https://share.freedoms4.org" target="_blank" rel="noopener noreferrer">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
draft = false
|
draft = false
|
||||||
title = 'XMPP'
|
title = 'XMPP'
|
||||||
weight = 2
|
weight = 2
|
||||||
|
registration-needed = true
|
||||||
+++
|
+++
|
||||||
|
|
||||||
<div id="xmpp-loggedin" style="display:none">
|
<div id="xmpp-loggedin" style="display:none">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1084,6 +1084,19 @@
|
|||||||
}
|
}
|
||||||
/* ── Auth pages (login / signup) ───────────────────────────────────────── */
|
/* ── Auth pages (login / signup) ───────────────────────────────────────── */
|
||||||
|
|
||||||
|
.auth-page__services {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
color: var(--foreground-color);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-group__title {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-page {
|
.auth-page {
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
margin: 2.5rem auto 0;
|
margin: 2.5rem auto 0;
|
||||||
@@ -1103,12 +1116,12 @@
|
|||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message banner — fixed height always reserved so the form never shifts */
|
/* Message banner — always in flow; min-height reserves space so the form never shifts */
|
||||||
.auth-message {
|
.auth-message {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
line-height: 1.3;
|
line-height: 1.4;
|
||||||
height: 1.8rem;
|
min-height: 1.8rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@@ -1116,10 +1129,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 0.15s ease;
|
transition: opacity 0.15s ease;
|
||||||
overflow: hidden;
|
padding: 0.25rem 0.7rem;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 0.7rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-message.is-visible {
|
.auth-message.is-visible {
|
||||||
@@ -1127,6 +1137,12 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-message--info {
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: var(--foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
.auth-message--error {
|
.auth-message--error {
|
||||||
background: rgba(255, 61, 61, 0.1);
|
background: rgba(255, 61, 61, 0.1);
|
||||||
border: 1px solid rgba(255, 61, 61, 0.4);
|
border: 1px solid rgba(255, 61, 61, 0.4);
|
||||||
@@ -1364,6 +1380,7 @@
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table th,
|
.admin-table th,
|
||||||
@@ -1373,9 +1390,9 @@
|
|||||||
border-bottom: 1px solid var(--background-color1, #e0e0e0);
|
border-bottom: 1px solid var(--background-color1, #e0e0e0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table th {
|
.admin-table__header th {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background: var(--background-color1, #f5f5f5);
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table tr:last-child td {
|
.admin-table tr:last-child td {
|
||||||
@@ -1405,6 +1422,30 @@
|
|||||||
color: #721c24;
|
color: #721c24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-search {
|
||||||
|
display: block;
|
||||||
|
max-width: 32rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search__label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search__input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.55rem 0.7rem;
|
||||||
|
border: 1px solid var(--background-color1, #ccc);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--foreground-color);
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-btn {
|
.admin-btn {
|
||||||
padding: 0.25rem 0.65rem;
|
padding: 0.25rem 0.65rem;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -5,4 +5,4 @@
|
|||||||
<a href=/signup/ class="auth-link auth-link--signup">Sign Up</a></div></div><div class=rss-subscribe><button class=rss-subscribe__btn aria-label="Subscribe via RSS" title="Subscribe via RSS">
|
<a href=/signup/ class="auth-link auth-link--signup">Sign Up</a></div></div><div class=rss-subscribe><button class=rss-subscribe__btn aria-label="Subscribe via RSS" title="Subscribe via RSS">
|
||||||
<svg viewBox="0 0 24 24" width="10" height="10" fill="currentColor" aria-hidden="true"><path d="M6.18 15.64a2.18 2.18.0 012.18 2.18c0 1.19-.98 2.18-2.18 2.18C4.98 20 4 19.01 4 17.82a2.18 2.18.0 012.18-2.18M4 4.44A15.56 15.56.0 0119.56 20h-2.83A12.73 12.73.0 004 7.27V4.44m0 5.66a9.9 9.9.0 019.9 9.9h-2.83A7.07 7.07.0 004 12.93V10.1z"/></svg>
|
<svg viewBox="0 0 24 24" width="10" height="10" fill="currentColor" aria-hidden="true"><path d="M6.18 15.64a2.18 2.18.0 012.18 2.18c0 1.19-.98 2.18-2.18 2.18C4.98 20 4 19.01 4 17.82a2.18 2.18.0 012.18-2.18M4 4.44A15.56 15.56.0 0119.56 20h-2.83A12.73 12.73.0 004 7.27V4.44m0 5.66a9.9 9.9.0 019.9 9.9h-2.83A7.07 7.07.0 004 12.93V10.1z"/></svg>
|
||||||
<span>Subscribe</span></button><div class=rss-subscribe__dropdown><button class=rss-subscribe__item data-rss-url=https://freedoms4.org/index.xml>All updates</button>
|
<span>Subscribe</span></button><div class=rss-subscribe__dropdown><button class=rss-subscribe__item data-rss-url=https://freedoms4.org/index.xml>All updates</button>
|
||||||
<button class=rss-subscribe__item data-rss-url=https://freedoms4.org/blog/index.xml>Blog posts</button></div></div></div></div><nav class="menu language"><ul class="menu__list language__list"><li class=menu__item><a class=menu__link href=/>Home</a></li><li class=menu__item><a class=menu__link href=/blog/>Blog</a></li><li class=menu__item><a class=menu__link href=/services/>Services</a></li><li class=menu__item><a class=menu__link href=/uninotes/>UniNotes</a></li><li class=menu__item><a class=menu__link href=/contact/>Contact</a></li></ul></nav></header><main class=main><nav class="uninotes-breadcrumbs breadcrumbs"><a href=/services/>Services</a></nav><h1>Services</h1><ol class="uninotes-list uninotes-list--subjects"><li class=uninotes-list__item><a class=uninotes-list__link href=/services/email-account/>Email</a></li><li class=uninotes-list__item><a class=uninotes-list__link href=/services/xmpp-account/>XMPP</a></li><li class=uninotes-list__item><a class=uninotes-list__link href=/services/file-share/>File Share</a></li></ol></main><footer class=footer><p class=footer__copyright-notice>© <a href=https://freedoms4.org>freedoms4.org</a> <a href=/changelog/>Changelog</a></p><p class=footer__theme-info>Built with <a href=https://gohugo.io>Hugo</a> and based on <a href=https://github.com/CyrusYip/hugo-theme-yue>Yue</a> theme</p></footer><script>(function(){document.querySelectorAll(".menu__item").forEach(function(e){var t=e.querySelector("a");t&&t.getAttribute("href")==="/uninotes/"&&e.classList.add("menu__item--uninotes")})})()</script><script>(function(){var e=document.getElementById("theme-toggle");if(!e)return;function t(){var e=localStorage.getItem("theme");return e?e:window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function n(e){document.documentElement.setAttribute("data-theme",e),localStorage.setItem("theme",e)}n(t()),e.addEventListener("click",function(){var e=document.documentElement.getAttribute("data-theme")||t();n(e==="dark"?"light":"dark")})})()</script><script>(function(){var t="https://backend.freedoms4.org/auth.php",e=localStorage.getItem("f4_username");if(!e)return;function n(e){var t,n,o,i,a,c,r=document.querySelector(".brand__auth");r&&!r.querySelector(".brand__auth-user")&&(r.classList.add("brand__auth--loggedin"),t=document.createElement("div"),t.className="brand__auth-user",n=document.createElement("button"),n.className="brand__auth-user-btn",n.setAttribute("aria-label","Account menu"),c=document.createElement("span"),c.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"/></svg>',n.appendChild(c.firstChild),n.appendChild(document.createTextNode(" "+e)),o=document.createElement("div"),o.className="brand__auth-user-dropdown",i=document.createElement("button"),i.className="brand__auth-user-logout",i.textContent="Log Out",i.addEventListener("click",s),e==="hyzen"&&(a=document.createElement("a"),a.className="brand__auth-user-admin",a.href="/admin/",a.textContent="User Management",o.appendChild(a)),o.appendChild(i),o.appendChild(i),t.appendChild(n),t.appendChild(o),r.appendChild(t),n.addEventListener("click",function(e){e.stopPropagation(),t.classList.toggle("is-open")}),document.addEventListener("click",function(){t.classList.remove("is-open")}))}function s(){fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({action:"logout"})}).finally(function(){localStorage.removeItem("f4_username"),localStorage.removeItem("f4_login_time"),localStorage.removeItem("f4_session_fails"),window.location.reload()})}n(e)})()</script><script>(function(){var t,e=document.querySelector(".rss-subscribe");if(!e)return;t=e.querySelector(".rss-subscribe__btn"),t.addEventListener("click",function(t){t.stopPropagation(),e.classList.toggle("is-open")}),document.addEventListener("click",function(t){e.contains(t.target)||e.classList.remove("is-open")}),e.querySelectorAll(".rss-subscribe__item").forEach(function(t){t.addEventListener("click",function(n){n.stopPropagation();var s,o=t.getAttribute("data-rss-url"),a=t.textContent;function i(){t.textContent="Copied!",setTimeout(function(){t.textContent=a,e.classList.remove("is-open")},1200)}try{s=document.createElement("textarea"),s.value=o,s.style.cssText="position:fixed;top:0;left:0;opacity:0;pointer-events:none;",document.body.appendChild(s),s.focus(),s.select(),document.execCommand("copy"),document.body.removeChild(s),i()}catch{navigator.clipboard&&navigator.clipboard.writeText(o).then(i).catch(function(){t.textContent=o})}})})})()</script></body></html>
|
<button class=rss-subscribe__item data-rss-url=https://freedoms4.org/blog/index.xml>Blog posts</button></div></div></div></div><nav class="menu language"><ul class="menu__list language__list"><li class=menu__item><a class=menu__link href=/>Home</a></li><li class=menu__item><a class=menu__link href=/blog/>Blog</a></li><li class=menu__item><a class=menu__link href=/services/>Services</a></li><li class=menu__item><a class=menu__link href=/uninotes/>UniNotes</a></li><li class=menu__item><a class=menu__link href=/contact/>Contact</a></li></ul></nav></header><main class=main><nav class="uninotes-breadcrumbs breadcrumbs"><a href=/services/>Services</a></nav><h1>Services</h1><h2 class=services-group__title>Need Registration</h2><ol class="uninotes-list uninotes-list--subjects"><li class=uninotes-list__item><a class=uninotes-list__link href=/services/email-account/>Email</a></li><li class=uninotes-list__item><a class=uninotes-list__link href=/services/xmpp-account/>XMPP</a></li></ol><h2 class=services-group__title>Without Registration</h2><ol class="uninotes-list uninotes-list--subjects"><li class=uninotes-list__item><a class=uninotes-list__link href=/services/file-share/>File Share</a></li></ol></main><footer class=footer><p class=footer__copyright-notice>© <a href=https://freedoms4.org>freedoms4.org</a> <a href=/changelog/>Changelog</a></p><p class=footer__theme-info>Built with <a href=https://gohugo.io>Hugo</a> and based on <a href=https://github.com/CyrusYip/hugo-theme-yue>Yue</a> theme</p></footer><script>(function(){document.querySelectorAll(".menu__item").forEach(function(e){var t=e.querySelector("a");t&&t.getAttribute("href")==="/uninotes/"&&e.classList.add("menu__item--uninotes")})})()</script><script>(function(){var e=document.getElementById("theme-toggle");if(!e)return;function t(){var e=localStorage.getItem("theme");return e?e:window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function n(e){document.documentElement.setAttribute("data-theme",e),localStorage.setItem("theme",e)}n(t()),e.addEventListener("click",function(){var e=document.documentElement.getAttribute("data-theme")||t();n(e==="dark"?"light":"dark")})})()</script><script>(function(){var t="https://backend.freedoms4.org/auth.php",e=localStorage.getItem("f4_username");if(!e)return;function n(e){var t,n,o,i,a,c,r=document.querySelector(".brand__auth");r&&!r.querySelector(".brand__auth-user")&&(r.classList.add("brand__auth--loggedin"),t=document.createElement("div"),t.className="brand__auth-user",n=document.createElement("button"),n.className="brand__auth-user-btn",n.setAttribute("aria-label","Account menu"),c=document.createElement("span"),c.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"/></svg>',n.appendChild(c.firstChild),n.appendChild(document.createTextNode(" "+e)),o=document.createElement("div"),o.className="brand__auth-user-dropdown",i=document.createElement("button"),i.className="brand__auth-user-logout",i.textContent="Log Out",i.addEventListener("click",s),e==="hyzen"&&(a=document.createElement("a"),a.className="brand__auth-user-admin",a.href="/admin/",a.textContent="User Management",o.appendChild(a)),o.appendChild(i),o.appendChild(i),t.appendChild(n),t.appendChild(o),r.appendChild(t),n.addEventListener("click",function(e){e.stopPropagation(),t.classList.toggle("is-open")}),document.addEventListener("click",function(){t.classList.remove("is-open")}))}function s(){fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({action:"logout"})}).finally(function(){localStorage.removeItem("f4_username"),localStorage.removeItem("f4_login_time"),localStorage.removeItem("f4_session_fails"),window.location.reload()})}n(e)})()</script><script>(function(){var t,e=document.querySelector(".rss-subscribe");if(!e)return;t=e.querySelector(".rss-subscribe__btn"),t.addEventListener("click",function(t){t.stopPropagation(),e.classList.toggle("is-open")}),document.addEventListener("click",function(t){e.contains(t.target)||e.classList.remove("is-open")}),e.querySelectorAll(".rss-subscribe__item").forEach(function(t){t.addEventListener("click",function(n){n.stopPropagation();var s,o=t.getAttribute("data-rss-url"),a=t.textContent;function i(){t.textContent="Copied!",setTimeout(function(){t.textContent=a,e.classList.remove("is-open")},1200)}try{s=document.createElement("textarea"),s.value=o,s.style.cssText="position:fixed;top:0;left:0;opacity:0;pointer-events:none;",document.body.appendChild(s),s.focus(),s.select(),document.execCommand("copy"),document.body.removeChild(s),i()}catch{navigator.clipboard&&navigator.clipboard.writeText(o).then(i).catch(function(){t.textContent=o})}})})})()</script></body></html>
|
||||||
File diff suppressed because one or more lines are too long
@@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
<div id="admin-panel" style="display: none">
|
<div id="admin-panel" style="display: none">
|
||||||
<h1>User Management</h1>
|
<h1>User Management</h1>
|
||||||
|
<label class="admin-search" for="admin-user-search">
|
||||||
|
<span class="admin-search__label">Search users</span>
|
||||||
|
<input
|
||||||
|
id="admin-user-search"
|
||||||
|
class="admin-search__input"
|
||||||
|
type="search"
|
||||||
|
placeholder="Search by username or email"
|
||||||
|
autocomplete="off"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
<div id="admin-msg" class="auth-message" style="display: none" aria-live="polite"></div>
|
<div id="admin-msg" class="auth-message" style="display: none" aria-live="polite"></div>
|
||||||
<div id="admin-table-wrap"></div>
|
<div id="admin-table-wrap"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -19,6 +29,8 @@
|
|||||||
var panel = document.getElementById('admin-panel');
|
var panel = document.getElementById('admin-panel');
|
||||||
var msg = document.getElementById('admin-msg');
|
var msg = document.getElementById('admin-msg');
|
||||||
var wrap = document.getElementById('admin-table-wrap');
|
var wrap = document.getElementById('admin-table-wrap');
|
||||||
|
var search = document.getElementById('admin-user-search');
|
||||||
|
var allUsers = [];
|
||||||
|
|
||||||
if (username !== 'hyzen') {
|
if (username !== 'hyzen') {
|
||||||
gate.style.display = '';
|
gate.style.display = '';
|
||||||
@@ -41,39 +53,37 @@
|
|||||||
|
|
||||||
function renderTable(users) {
|
function renderTable(users) {
|
||||||
if (!users.length) {
|
if (!users.length) {
|
||||||
wrap.innerHTML = '<p>No users found.</p>';
|
wrap.innerHTML = '<p>No matching users found.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var html =
|
var html =
|
||||||
'<table class="admin-table">' +
|
'<table class="admin-table">' +
|
||||||
'<thead><tr>' +
|
'<tbody><tr class="admin-table__header">' +
|
||||||
'<th>#</th><th>Username</th><th>Email</th><th>Joined</th><th>Status</th><th>Actions</th>' +
|
'<th>#</th><th>Username</th><th>Email</th><th>Joined</th><th>Status</th><th>Actions</th>' +
|
||||||
'</tr></thead><tbody>';
|
'</tr>';
|
||||||
|
|
||||||
users.forEach(function (u) {
|
function userRow(u) {
|
||||||
var joined = new Date(u.created_at).toLocaleDateString();
|
var joined = new Date(u.created_at).toLocaleDateString();
|
||||||
var blocked = u.blocked === true || u.blocked === 't' || u.blocked === '1';
|
var blocked = u.blocked === true || u.blocked === 't' || u.blocked === '1';
|
||||||
var status = blocked
|
var status = blocked
|
||||||
? '<span class="admin-badge admin-badge--blocked">Blocked</span>'
|
? '<span class="admin-badge admin-badge--blocked">Blocked</span>'
|
||||||
: '<span class="admin-badge admin-badge--active">Active</span>';
|
: '<span class="admin-badge admin-badge--active">Active</span>';
|
||||||
|
|
||||||
var blockBtn = blocked
|
var blockBtn = blocked
|
||||||
? '<button class="admin-btn admin-btn--unblock" data-id="' +
|
? '<button class="admin-btn admin-btn--unblock" data-id="' +
|
||||||
u.id +
|
u.id +
|
||||||
'" data-action="unblock_user">Unblock</button>'
|
'" data-action="unblock_user">Unblock</button>'
|
||||||
: '<button class="admin-btn admin-btn--block" data-id="' +
|
: '<button class="admin-btn admin-btn--block" data-id="' +
|
||||||
u.id +
|
u.id +
|
||||||
'" data-action="block_user">Block</button>';
|
'" data-action="block_user">Block</button>';
|
||||||
|
|
||||||
var deleteBtn =
|
var deleteBtn =
|
||||||
'<button class="admin-btn admin-btn--delete" data-id="' +
|
'<button class="admin-btn admin-btn--delete" data-id="' +
|
||||||
u.id +
|
u.id +
|
||||||
'" data-username="' +
|
'" data-username="' +
|
||||||
escHtml(u.username) +
|
escHtml(u.username) +
|
||||||
'" data-action="delete_user">Delete</button>';
|
'" data-action="delete_user">Delete Records</button>';
|
||||||
|
|
||||||
html +=
|
return (
|
||||||
'<tr id="user-row-' +
|
'<tr id="user-row-' +
|
||||||
u.id +
|
u.id +
|
||||||
'">' +
|
'">' +
|
||||||
@@ -97,7 +107,12 @@
|
|||||||
' ' +
|
' ' +
|
||||||
deleteBtn +
|
deleteBtn +
|
||||||
'</td>' +
|
'</td>' +
|
||||||
'</tr>';
|
'</tr>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
users.forEach(function (u) {
|
||||||
|
html += userRow(u);
|
||||||
});
|
});
|
||||||
|
|
||||||
html += '</tbody></table>';
|
html += '</tbody></table>';
|
||||||
@@ -116,6 +131,13 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
} else if (action === 'block_user') {
|
||||||
|
if (
|
||||||
|
!confirm(
|
||||||
|
"This will permanently delete the user's email history and cannot be undone"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
@@ -132,11 +154,12 @@
|
|||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
if (action === 'delete_user') {
|
if (action === 'delete_user') {
|
||||||
var row = document.getElementById('user-row-' + id);
|
allUsers = allUsers.filter(function (u) {
|
||||||
if (row) row.remove();
|
return Number(u.id) !== id;
|
||||||
|
});
|
||||||
|
filterUsers();
|
||||||
showMsg('User deleted.', 'success');
|
showMsg('User deleted.', 'success');
|
||||||
} else {
|
} else {
|
||||||
// Reload to reflect new block state
|
|
||||||
loadUsers();
|
loadUsers();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -162,7 +185,8 @@
|
|||||||
})
|
})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
renderTable(data.users);
|
allUsers = data.users;
|
||||||
|
filterUsers();
|
||||||
} else {
|
} else {
|
||||||
wrap.innerHTML =
|
wrap.innerHTML =
|
||||||
'<p class="admin-error">' +
|
'<p class="admin-error">' +
|
||||||
@@ -175,6 +199,20 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterUsers() {
|
||||||
|
var query = search.value.trim().toLowerCase();
|
||||||
|
var users = allUsers.filter(function (u) {
|
||||||
|
return (
|
||||||
|
u.username !== username &&
|
||||||
|
(!query ||
|
||||||
|
String(u.username).toLowerCase().includes(query) ||
|
||||||
|
String(u.email).toLowerCase().includes(query))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
renderTable(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
search.addEventListener('input', filterUsers);
|
||||||
loadUsers();
|
loadUsers();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,12 +6,36 @@
|
|||||||
|
|
||||||
<h1>Services</h1>
|
<h1>Services</h1>
|
||||||
|
|
||||||
|
{{ $registrationServices := slice }}
|
||||||
|
{{ $openServices := slice }}
|
||||||
|
{{ range .Pages.ByWeight }}
|
||||||
|
{{ if index .Params "registration-needed" }}
|
||||||
|
{{ $registrationServices = $registrationServices | append . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $openServices = $openServices | append . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with $registrationServices }}
|
||||||
|
<h2 class="services-group__title">Need Registration</h2>
|
||||||
<ol class="uninotes-list uninotes-list--subjects">
|
<ol class="uninotes-list uninotes-list--subjects">
|
||||||
{{ range .Pages.ByWeight }}
|
{{ range . }}
|
||||||
<li class="uninotes-list__item">
|
<li class="uninotes-list__item">
|
||||||
<a class="uninotes-list__link" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
<a class="uninotes-list__link" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ol>
|
</ol>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with $openServices }}
|
||||||
|
<h2 class="services-group__title">Without Registration</h2>
|
||||||
|
<ol class="uninotes-list uninotes-list--subjects">
|
||||||
|
{{ range . }}
|
||||||
|
<li class="uninotes-list__item">
|
||||||
|
<a class="uninotes-list__link" href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ol>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -2,7 +2,23 @@
|
|||||||
<div class="auth-page">
|
<div class="auth-page">
|
||||||
<div class="auth-card">
|
<div class="auth-card">
|
||||||
<h1 class="auth-page__title">{{ .Title }}</h1>
|
<h1 class="auth-page__title">{{ .Title }}</h1>
|
||||||
|
{{- with site.GetPage "/services" -}} {{- $registrationServices := slice -}} {{- range
|
||||||
|
.Pages.ByWeight -}} {{- if index .Params "registration-needed" -}} {{- $registrationServices
|
||||||
|
= $registrationServices | append . -}} {{- end -}} {{- end -}} {{- with
|
||||||
|
$registrationServices -}} {{- $serviceLinks := slice -}} {{- range . -}} {{- $serviceLinks =
|
||||||
|
$serviceLinks | append (printf `<a href="%s">%s</a>` .RelPermalink .LinkTitle) -}} {{- end
|
||||||
|
-}}
|
||||||
|
<div
|
||||||
|
id="auth-message"
|
||||||
|
class="auth-message auth-message--info is-visible"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
Signing up is giving you access to the following services: {{ delimit $serviceLinks ", "
|
||||||
|
| safeHTML }}
|
||||||
|
</div>
|
||||||
|
{{- end -}} {{- if not $registrationServices -}}
|
||||||
<div id="auth-message" class="auth-message" aria-live="polite"></div>
|
<div id="auth-message" class="auth-message" aria-live="polite"></div>
|
||||||
|
{{- end -}} {{- end -}}
|
||||||
|
|
||||||
<!-- ── STEP 1 — Account details ───────────────────────────────── -->
|
<!-- ── STEP 1 — Account details ───────────────────────────────── -->
|
||||||
<form id="signup-form" class="auth-form" novalidate>
|
<form id="signup-form" class="auth-form" novalidate>
|
||||||
@@ -218,12 +234,21 @@
|
|||||||
var pwdInput = document.getElementById('signup-password');
|
var pwdInput = document.getElementById('signup-password');
|
||||||
var eyeBtn = signupForm.querySelector('.auth-form__eye');
|
var eyeBtn = signupForm.querySelector('.auth-form__eye');
|
||||||
|
|
||||||
|
// Snapshot the server-rendered services notice so we can restore it after errors
|
||||||
|
var _servicesHTML = msgBox.innerHTML;
|
||||||
|
var _servicesClass = msgBox.className;
|
||||||
|
|
||||||
|
// Pin the box height to whatever the services text renders at, so switching
|
||||||
|
// to a one-line error message never shrinks the box and causes layout shift.
|
||||||
|
msgBox.style.minHeight = msgBox.offsetHeight + 'px';
|
||||||
|
|
||||||
function showMsg(text, type) {
|
function showMsg(text, type) {
|
||||||
msgBox.textContent = text;
|
msgBox.textContent = text;
|
||||||
msgBox.className = 'auth-message auth-message--' + type + ' is-visible';
|
msgBox.className = 'auth-message auth-message--' + type + ' is-visible';
|
||||||
}
|
}
|
||||||
function hideMsg() {
|
function hideMsg() {
|
||||||
msgBox.className = 'auth-message';
|
msgBox.innerHTML = _servicesHTML;
|
||||||
|
msgBox.className = _servicesClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLoading(btn, on) {
|
function setLoading(btn, on) {
|
||||||
|
|||||||
@@ -1084,6 +1084,19 @@
|
|||||||
}
|
}
|
||||||
/* ── Auth pages (login / signup) ───────────────────────────────────────── */
|
/* ── Auth pages (login / signup) ───────────────────────────────────────── */
|
||||||
|
|
||||||
|
.auth-page__services {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
color: var(--foreground-color);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.services-group__title {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-page {
|
.auth-page {
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
margin: 2.5rem auto 0;
|
margin: 2.5rem auto 0;
|
||||||
@@ -1103,12 +1116,12 @@
|
|||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Message banner — fixed height always reserved so the form never shifts */
|
/* Message banner — always in flow; min-height reserves space so the form never shifts */
|
||||||
.auth-message {
|
.auth-message {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
line-height: 1.3;
|
line-height: 1.4;
|
||||||
height: 1.8rem;
|
min-height: 1.8rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
@@ -1116,10 +1129,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 0.15s ease;
|
transition: opacity 0.15s ease;
|
||||||
overflow: hidden;
|
padding: 0.25rem 0.7rem;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 0.7rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-message.is-visible {
|
.auth-message.is-visible {
|
||||||
@@ -1127,6 +1137,12 @@
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.auth-message--info {
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: var(--foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
.auth-message--error {
|
.auth-message--error {
|
||||||
background: rgba(255, 61, 61, 0.1);
|
background: rgba(255, 61, 61, 0.1);
|
||||||
border: 1px solid rgba(255, 61, 61, 0.4);
|
border: 1px solid rgba(255, 61, 61, 0.4);
|
||||||
@@ -1364,6 +1380,7 @@
|
|||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-size: 0.88rem;
|
font-size: 0.88rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table th,
|
.admin-table th,
|
||||||
@@ -1373,9 +1390,9 @@
|
|||||||
border-bottom: 1px solid var(--background-color1, #e0e0e0);
|
border-bottom: 1px solid var(--background-color1, #e0e0e0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table th {
|
.admin-table__header th {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background: var(--background-color1, #f5f5f5);
|
background: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-table tr:last-child td {
|
.admin-table tr:last-child td {
|
||||||
@@ -1405,6 +1422,30 @@
|
|||||||
color: #721c24;
|
color: #721c24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-search {
|
||||||
|
display: block;
|
||||||
|
max-width: 32rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search__label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search__input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.55rem 0.7rem;
|
||||||
|
border: 1px solid var(--background-color1, #ccc);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--foreground-color);
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.admin-btn {
|
.admin-btn {
|
||||||
padding: 0.25rem 0.65rem;
|
padding: 0.25rem 0.65rem;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user