Added resizable/scrollable lists on admin panel

This commit is contained in:
rainbow napkin 2026-10-28 20:18:10 -04:00
parent a9f083dcc6
commit 3c37080cb7
9 changed files with 156 additions and 134 deletions

View file

@ -15,36 +15,40 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. %> along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<div id="admin-channel-list-div" class="admin-list-div"> <div id="admin-channel-list-div" class="admin-list-div">
<h3>Channel List:</h3> <h3>Channel List:</h3>
<div class="dynamic-container"> <div class="dynamic-container resizable table-container">
<table id="admin-channel-list-table" class="admin-list-table"> <table id="admin-channel-list-table" class="admin-list-table">
<tr id="admin-channel-list-entry-title" class="admin-list-entry-title-row"> <thead>
<td id="admin-channel-list-entry-img-title" class=" admin-list-entry-title admin-list-entry-item admin-list-entry-img-title"> <tr id="admin-channel-list-entry-title" class="admin-list-entry-title-row">
<h3>Img</h3> <td id="admin-channel-list-entry-img-title" class=" admin-list-entry-title admin-list-entry-item admin-list-entry-img-title">
</td> <h3>Img</h3>
<td id="admin-channel-list-entry-name-title" class=" admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Name</h3> <td id="admin-channel-list-entry-name-title" class=" admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Name</h3>
<td id="admin-channel-list-entry-description-title" class=" admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Description</h3> <td id="admin-channel-list-entry-description-title" class=" admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Description</h3>
</tr> </td>
<% chanGuide.forEach((channel) => { %> </tr>
<tr id="admin-channel-list-entry-<%= unescape(channel.name) %>" class=""> </thead>
<td id="admin-channel-list-entry-img-<%= unescape(channel.name) %>" class=" admin-list-entry-item"> <tbody>
<a href="/c/<%= encodeURI(unescape(channel.name)) %>" class=" admin-list-entry-item"> <% chanGuide.forEach((channel) => { %>
<img id="admin-channel-list-entry-img-<%= unescape(channel.name) %>" class=" admin-list-entry-item" src="<%= encodeURI(unescape(channel.thumbnail)) %>"> <tr id="admin-channel-list-entry-<%= unescape(channel.name) %>" class="">
</a> <td id="admin-channel-list-entry-img-<%= unescape(channel.name) %>" class=" admin-list-entry-item">
</td> <a href="/c/<%= encodeURI(unescape(channel.name)) %>" class=" admin-list-entry-item">
<td id="admin-channel-list-entry-name-<%= unescape(channel.name) %>" class=" admin-list-entry-item not-first-col"> <img id="admin-channel-list-entry-img-<%= unescape(channel.name) %>" class=" admin-list-entry-item" src="<%= encodeURI(unescape(channel.thumbnail)) %>">
<a href="/c/<%= unescape(channel.name) %>" class=" admin-list-entry-item"> </a>
<%= unescape(channel.name) %> </td>
</a> <td id="admin-channel-list-entry-name-<%= unescape(channel.name) %>" class=" admin-list-entry-item not-first-col">
</td> <a href="/c/<%= unescape(channel.name) %>" class=" admin-list-entry-item">
<td id="admin-channel-list-entry-description-<%= unescape(channel.name) %>" class="large admin-list-entry-item not-first-col"> <%= unescape(channel.name) %>
<%= unescape(channel.description) %> </a>
</td> </td>
</tr> <td id="admin-channel-list-entry-description-<%= unescape(channel.name) %>" class="large admin-list-entry-item not-first-col">
<% }); %> <%= unescape(channel.description) %>
</td>
</tr>
<% }); %>
</tbody>
</table> </table>
</div> </div>
</div> </div>

View file

@ -23,6 +23,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<div class="control-prompt"> <div class="control-prompt">
<input placeholder="Emote Link..." id="new-emote-link-input" class="control-prompt"> <input placeholder="Emote Link..." id="new-emote-link-input" class="control-prompt">
</div> </div>
<div class="dynamic-container" id="emote-list"> <div class="dynamic-container resizable" id="emote-list">
</div> </div>
</div> </div>

View file

@ -19,6 +19,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
!<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt"> !<input placeholder="Add Command..." id="new-toke-command-input" class="control-prompt">
<button id="new-toke-command-button" class="positive-button">Add</button> <button id="new-toke-command-button" class="positive-button">Add</button>
</div> </div>
<div class="dynamic-container toke-command-list"> <div class="dynamic-container toke-command-list resizable">
</div> </div>
</div> </div>

View file

@ -15,34 +15,38 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. %> along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<div id="admin-ban-list-div" class="admin-list-div"> <div id="admin-ban-list-div" class="admin-list-div">
<h3>User Ban List:</h3> <h3>User Ban List:</h3>
<div class="dynamic-container"> <div class="dynamic-container resizable table-container">
<table id="admin-ban-list-table" class="admin-list-table"> <table id="admin-ban-list-table" class="admin-list-table">
<tr id="admin-ban-list-entry-title" class="admin-list-entry-title-row"> <thead>
<td id="admin-ban-list-entry-user-title" class="admin-list-entry-title admin-list-entry-item"> <tr id="admin-ban-list-entry-title" class="admin-list-entry-title-row">
<h3>Main<br>User</h3> <td id="admin-ban-list-entry-user-title" class="admin-list-entry-title admin-list-entry-item">
</td> <h3>Main<br>User</h3>
<td id="admin-ban-list-entry-alts-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Alt<br>Users</h3> <td id="admin-ban-list-entry-alts-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Alt<br>Users</h3>
<td id="admin-ban-list-entry-ban-nuked-users-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Nuked<br>Users</h3> <td id="admin-ban-list-entry-ban-nuked-users-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Nuked<br>Users</h3>
<td id="admin-ban-list-entry-ban-ips-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Banned<br>IPs</h3> <td id="admin-ban-list-entry-ban-ips-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Banned<br>IPs</h3>
<td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Ban<br>Date</h3> <td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Ban<br>Date</h3>
<td id="admin-ban-list-entry-expiration-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Expires</h3> <td id="admin-ban-list-entry-expiration-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Expires</h3>
<td id="admin-ban-list-entry-expiration-type-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Expiration<br>Action</h3> <td id="admin-ban-list-entry-expiration-type-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Expiration<br>Action</h3>
<td id="admin-ban-list-entry-actions-type-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
&nbsp; <td id="admin-ban-list-entry-actions-type-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> &nbsp;
</tr> </td>
</tr>
</thead>
<tbody>
</tbody>
</table> </table>
</div> </div>
</div> </div>

View file

@ -15,71 +15,75 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. %> along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<div id="admin-user-list-div" class="admin-list-div"> <div id="admin-user-list-div" class="admin-list-div">
<h3>User List:</h3> <h3>User List:</h3>
<div class="dynamic-container resizable"> <div class="dynamic-container resizable table-container">
<table id="admin-user-list-table" class="admin-list-table"> <table id="admin-user-list-table" class="admin-list-table">
<tr id="admin-user-list-entry-title" class="admin-list-entry-title-row"> <thead>
<td id="admin-user-list-entry-img-title" class="admin-list-entry-title admin-list-entry-item admin-list-entry-img-title"> <tr id="admin-user-list-entry-title" class="admin-list-entry-title-row">
<h3>Img</h3> <td id="admin-user-list-entry-img-title" class="admin-list-entry-title admin-list-entry-item admin-list-entry-img-title">
</td> <h3>Img</h3>
<td id="admin-user-list-entry-id-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>ID</h3> <td id="admin-user-list-entry-id-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>ID</h3>
<td id="admin-user-list-entry-name-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Name</h3> <td id="admin-user-list-entry-name-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Name</h3>
<td id="admin-user-list-entry-rank-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Rank</h3> <td id="admin-user-list-entry-rank-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Rank</h3>
<td id="admin-user-list-entry-mail-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>E-Mail</h3> <td id="admin-user-list-entry-mail-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>E-Mail</h3>
<td id="admin-user-list-entry-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
<h3>Sign-Up<br>Date</h3> <td id="admin-user-list-entry-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> <h3>Sign-Up<br>Date</h3>
<td id="admin-user-list-entry-control-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> </td>
&nbsp; <td id="admin-user-list-entry-control-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
</td> &nbsp;
</tr> </td>
<% userList.forEach((curUser) => { %> </tr>
<tr id="admin-user-list-entry-<%= unescape(curUser.user) %>" class="admin-list-entry" data-name="<%= unescape(curUser.user) %>" > </thead>
<td class="admin-list-entry-item"> <tbody>
<a href="/profile/<%= unescape(curUser.user) %>" class="admin-list-entry-item"> <% userList.forEach((curUser) => { %>
<img class="admin-list-entry-item" src="<%= encodeURI(unescape(curUser.img)) %>"> <tr id="admin-user-list-entry-<%= unescape(curUser.user) %>" class="admin-list-entry" data-name="<%= unescape(curUser.user) %>" >
</a> <td class="admin-list-entry-item">
</td> <a href="/profile/<%= unescape(curUser.user) %>" class="admin-list-entry-item">
<td class="admin-list-entry-item not-first-col"> <img class="admin-list-entry-item" src="<%= encodeURI(unescape(curUser.img)) %>">
<a href="/profile/<%= encodeURI(unescape(curUser.user)) %>" class="admin-list-entry-item"> </a>
<%= curUser.id %> </td>
</a> <td class="admin-list-entry-item not-first-col">
</td> <a href="/profile/<%= encodeURI(unescape(curUser.user)) %>" class="admin-list-entry-item">
<td class="admin-list-entry-item not-first-col"> <%= curUser.id %>
<a href="/profile/<%= encodeURI(unescape(curUser.user)) %>" class="admin-list-entry-item admin-user-list-name"> </a>
<%= unescape(curUser.user) %> </td>
</a> <td class="admin-list-entry-item not-first-col">
</td> <a href="/profile/<%= encodeURI(unescape(curUser.user)) %>" class="admin-list-entry-item admin-user-list-name">
<td class="admin-list-entry-item not-first-col"> <%= unescape(curUser.user) %>
<% if(rankEnum.indexOf(curUser.rank) < rankEnum.indexOf(user.rank)){%> </a>
<select id="admin-user-list-rank-select-<%= unescape(curUser.user) %>" class="admin-user-list-rank-select"> </td>
<%rankEnum.slice().reverse().forEach((rank)=>{ %> <td class="admin-list-entry-item not-first-col">
<option <%if(curUser.rank == rank){%> selected <%}%> value="<%= rank %>"><%= rank %></option> <% if(rankEnum.indexOf(curUser.rank) < rankEnum.indexOf(user.rank)){%>
<% }); %> <select id="admin-user-list-rank-select-<%= unescape(curUser.user) %>" class="admin-user-list-rank-select">
</select> <%rankEnum.slice().reverse().forEach((rank)=>{ %>
<% }else{ %> <option <%if(curUser.rank == rank){%> selected <%}%> value="<%= rank %>"><%= rank %></option>
<%= curUser.rank %> <% }); %>
<% } %> </select>
</td> <% }else{ %>
<td class="admin-list-entry-item not-first-col"> <%= curUser.rank %>
<%= unescape(curUser.email) ? curUser.email : "N/A" %> <% } %>
</td> </td>
<td class="admin-list-entry-item not-first-col"> <td class="admin-list-entry-item not-first-col">
<%= unescape(curUser.date.toUTCString()) %> <%= unescape(curUser.email) ? curUser.email : "N/A" %>
</td> </td>
<td class="admin-list-entry-item not-first-col"> <td class="admin-list-entry-item not-first-col">
<%# It's either this or add whitespce >:( %> <%= unescape(curUser.date.toUTCString()) %>
<i class="bi-radioactive admin-user-list-icon admin-user-list-nuke-icon" title="Nuke Account: <%= unescape(curUser.user) %>"></i><i class="bi-fire admin-user-list-icon admin-user-list-ban-icon" title="Ban User: <%= unescape(curUser.user) %>"></i><i class="bi-arrow-clockwise admin-user-list-icon admin-user-list-pw-reset-icon" title="Generate Password Reset Link for <%= unescape(curUser.user) %>"></i> </td>
</td> <td class="admin-list-entry-item not-first-col">
</tr> <%# It's either this or add whitespce >:( %>
<% }); %> <i class="bi-radioactive admin-user-list-icon admin-user-list-nuke-icon" title="Nuke Account: <%= unescape(curUser.user) %>"></i><i class="bi-fire admin-user-list-icon admin-user-list-ban-icon" title="Ban User: <%= unescape(curUser.user) %>"></i><i class="bi-arrow-clockwise admin-user-list-icon admin-user-list-pw-reset-icon" title="Generate Password Reset Link for <%= unescape(curUser.user) %>"></i>
</td>
</tr>
<% }); %>
</tbody>
</table> </table>
</div> </div>
</div> </div>

View file

@ -71,10 +71,9 @@ div.dynamic-container, div.nested-dynamic-container{
div.dynamic-container.resizable{ div.dynamic-container.resizable{
resize: vertical; resize: vertical;
width: fit-content;
height: fit-content; height: fit-content;
min-height: 1.5em; min-height: 1.5em;
max-height: 8em; max-height: 17.5em;
padding: 0.5em 0.2em; padding: 0.5em 0.2em;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
@ -84,6 +83,15 @@ div.dynamic-container.resizable[style*="height"]{
max-height: 100%; max-height: 100%;
} }
div.dynamic-container.table-container{
padding-top: 0;
}
div.dynamic-container.table-container table thead{
position: sticky;
top: 0;
}
.interactive{ .interactive{
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;

View file

@ -257,6 +257,10 @@ textarea{
color: var(--accent1); color: var(--accent1);
} }
div.dynamic-container.table-container table thead{
background-color: var(--bg1);
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px 1em 1px -1em;
}
tr{ tr{
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em; box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em;

View file

@ -277,6 +277,11 @@ textarea{
color: var(--accent1); color: var(--accent1);
} }
div.dynamic-container.table-container table thead{
background-color: var(--bg2-alt1);
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px 1em 1px -1em;
}
tr{ tr{
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em; box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em;
} }

View file

@ -143,7 +143,7 @@ class adminPermissionList{
class adminUserBanList{ class adminUserBanList{
constructor(){ constructor(){
this.table = document.querySelector("#admin-ban-list-table"); this.tbody = document.querySelector("#admin-ban-list-table tbody");
this.getBanList(); this.getBanList();
} }
@ -152,13 +152,6 @@ class adminUserBanList{
this.renderBanList(await adminUtil.getBans()); this.renderBanList(await adminUtil.getBans());
} }
clearBanList(){
const oldRows = this.table.querySelectorAll('tr.gen-row');
oldRows.forEach((row) => {
row.remove();
});
}
async unban(event){ async unban(event){
//Get username from target id //Get username from target id
const user = event.target.dataset.name; const user = event.target.dataset.name;
@ -168,7 +161,7 @@ class adminUserBanList{
renderBanList(banList){ renderBanList(banList){
//Clear out the ban list //Clear out the ban list
this.clearBanList(); this.tbody.innerHTML = "";
//For each ban received //For each ban received
banList.forEach((ban) => { banList.forEach((ban) => {
@ -195,7 +188,7 @@ class adminUserBanList{
} }
//Generate and append row to table //Generate and append row to table
this.table.appendChild(utils.ux.newTableRow([ this.tbody.appendChild(utils.ux.newTableRow([
this.renderUser(ban.user, nuked), this.renderUser(ban.user, nuked),
this.renderUsers(ban.alts, nuked), this.renderUsers(ban.alts, nuked),
this.renderDeadUsers(ban.deletedNames), this.renderDeadUsers(ban.deletedNames),