chore: clean up admin panel

This commit is contained in:
Lapis
2026-06-19 16:59:00 -04:00
parent 3794e6e0ab
commit 21b5ac84e9
15 changed files with 442 additions and 329 deletions
@@ -9,7 +9,7 @@ main {
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
background: white;
}
.top-row {
@@ -21,27 +21,30 @@ main {
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a,
.top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:hover,
.top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
.top-row ::deep a,
.top-row ::deep .btn-link {
margin-left: 0;
}
}
@@ -70,7 +73,8 @@ main {
width: 0;
}
.top-row, article {
.top-row,
article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
@@ -90,9 +94,9 @@ main {
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
@@ -1,53 +1,36 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">CannedNet.Admin</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="nav flex-column">
<AuthorizeView>
<Authorized>
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Dashboard
</NavLink>
</div>
<div class="nav-section-label px-3 mt-3 mb-1 text-muted small text-uppercase">Users</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="users/search">
<span class="bi bi-search-nav-menu" aria-hidden="true"></span> Search Users
</NavLink>
</div>
<div class="nav-section-label px-3 mt-3 mb-1 text-muted small text-uppercase">Rooms</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="rooms/search">
<span class="bi bi-building-nav-menu" aria-hidden="true"></span> Search Rooms
</NavLink>
</div>
<div class="nav-section-label px-3 mt-3 mb-1 text-muted small text-uppercase">Config</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="config/gameconfig">
<span class="bi bi-sliders-nav-menu" aria-hidden="true"></span> Game Config
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="config/communityboard">
<span class="bi bi-megaphone-fill-nav-menu" aria-hidden="true"></span> Community Board
</NavLink>
</div>
<div class="nav-section-label px-3 mt-3 mb-1 text-muted small text-uppercase">Storefronts</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="storefronts">
<span class="bi bi-cart-fill-nav-menu" aria-hidden="true"></span> Storefronts
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="settings">
<span class="bi bi-gear-fill-nav-menu" aria-hidden="true"></span> Settings
</NavLink>
</div>
</Authorized>
</AuthorizeView>
</nav>
@@ -1,29 +1,3 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
@@ -39,55 +13,42 @@
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:first-of-type {
padding-top: 0.75rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 0;
}
.nav-item ::deep a {
color: #d7d7d7;
border-radius: 4px;
height: 2.5rem;
display: flex;
align-items: center;
line-height: 2.5rem;
}
.nav-item ::deep a {
color: #000;
border-radius: 0.5px;
height: 2.5rem;
display: flex;
align-items: center;
line-height: 2.5rem;
justify-content: flex-start;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep a.active {
background-color: rgba(0, 0, 0, 0.08);
color: #000;
}
.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-item ::deep a:hover {
background-color: rgba(0, 0, 0, 0.05);
color: #000;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
.nav-section-label {
font-size: 0.7rem;
letter-spacing: 0.05em;
opacity: 0.7;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
display: block;
height: calc(100vh - 3.5rem);
height: 100vh;
overflow-y: auto;
}
}
@@ -7,19 +7,25 @@
display: none;
}
#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible,
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-paused .components-pause-visible,
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible,
#components-reconnect-modal.components-reconnect-show
.components-reconnect-first-attempt-visible,
#components-reconnect-modal.components-reconnect-show
.components-rejoining-animation,
#components-reconnect-modal.components-reconnect-paused
.components-pause-visible,
#components-reconnect-modal.components-reconnect-resume-failed
.components-resume-failed-visible,
#components-reconnect-modal.components-reconnect-retrying,
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible,
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation,
#components-reconnect-modal.components-reconnect-retrying
.components-reconnect-repeated-attempt-visible,
#components-reconnect-modal.components-reconnect-retrying
.components-rejoining-animation,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible {
#components-reconnect-modal.components-reconnect-failed
.components-reconnect-failed-visible {
display: block;
}
#components-reconnect-modal {
background-color: white;
width: 20rem;
@@ -29,15 +35,17 @@
border-radius: 0.5rem;
box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3);
opacity: 0;
transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete;
transition:
display 0.5s allow-discrete,
overlay 0.5s allow-discrete;
animation: components-reconnect-modal-fadeOutOpacity 0.5s both;
&[open]
{
animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
animation-fill-mode: both;
}
&[open] {
animation:
components-reconnect-modal-slideUp 1.5s
cubic-bezier(0.05, 0.89, 0.25, 1.02) 0.3s,
components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s;
animation-fill-mode: both;
}
}
#components-reconnect-modal::backdrop {
@@ -96,13 +104,13 @@
border-radius: 4px;
}
#components-reconnect-modal button:hover {
background-color: #3b6ea2;
}
#components-reconnect-modal button:hover {
background-color: #3b6ea2;
}
#components-reconnect-modal button:active {
background-color: #6b9ed2;
}
#components-reconnect-modal button:active {
background-color: #6b9ed2;
}
.components-rejoining-animation {
position: relative;
@@ -110,17 +118,18 @@
height: 80px;
}
.components-rejoining-animation div {
position: absolute;
border: 3px solid #0087ff;
opacity: 1;
border-radius: 50%;
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.components-rejoining-animation div {
position: absolute;
border: 3px solid #0087ff;
opacity: 1;
border-radius: 50%;
animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1)
infinite;
}
.components-rejoining-animation div:nth-child(2) {
animation-delay: -0.5s;
}
.components-rejoining-animation div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes components-rejoining-animation {
0% {
@@ -1,6 +1,9 @@
// Set up event handlers
const reconnectModal = document.getElementById("components-reconnect-modal");
reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged);
reconnectModal.addEventListener(
"components-reconnect-state-changed",
handleReconnectStateChanged,
);
const retryButton = document.getElementById("components-reconnect-button");
retryButton.addEventListener("click", retry);
@@ -9,55 +12,67 @@ const resumeButton = document.getElementById("components-resume-button");
resumeButton.addEventListener("click", resume);
function handleReconnectStateChanged(event) {
if (event.detail.state === "show") {
reconnectModal.showModal();
} else if (event.detail.state === "hide") {
reconnectModal.close();
} else if (event.detail.state === "failed") {
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
} else if (event.detail.state === "rejected") {
location.reload();
}
if (event.detail.state === "show") {
reconnectModal.showModal();
} else if (event.detail.state === "hide") {
reconnectModal.close();
} else if (event.detail.state === "failed") {
document.addEventListener(
"visibilitychange",
retryWhenDocumentBecomesVisible,
);
} else if (event.detail.state === "rejected") {
location.reload();
}
}
async function retry() {
document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
document.removeEventListener(
"visibilitychange",
retryWhenDocumentBecomesVisible,
);
try {
// Reconnect will asynchronously return:
// - true to mean success
// - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
// - exception to mean we didn't reach the server (this can be sync or async)
const successful = await Blazor.reconnect();
if (!successful) {
// We have been able to reach the server, but the circuit is no longer available.
// We'll reload the page so the user can continue using the app as quickly as possible.
const resumeSuccessful = await Blazor.resumeCircuit();
if (!resumeSuccessful) {
location.reload();
} else {
reconnectModal.close();
}
}
} catch (err) {
// We got an exception, server is currently unavailable
document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible);
try {
// Reconnect will asynchronously return:
// - true to mean success
// - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID)
// - exception to mean we didn't reach the server (this can be sync or async)
const successful = await Blazor.reconnect();
if (!successful) {
// We have been able to reach the server, but the circuit is no longer available.
// We'll reload the page so the user can continue using the app as quickly as possible.
const resumeSuccessful = await Blazor.resumeCircuit();
if (!resumeSuccessful) {
location.reload();
} else {
reconnectModal.close();
}
}
} catch (err) {
// We got an exception, server is currently unavailable
document.addEventListener(
"visibilitychange",
retryWhenDocumentBecomesVisible,
);
}
}
async function resume() {
try {
const successful = await Blazor.resumeCircuit();
if (!successful) {
location.reload();
}
} catch {
reconnectModal.classList.replace("components-reconnect-paused", "components-reconnect-resume-failed");
try {
const successful = await Blazor.resumeCircuit();
if (!successful) {
location.reload();
}
} catch {
reconnectModal.classList.replace(
"components-reconnect-paused",
"components-reconnect-resume-failed",
);
}
}
async function retryWhenDocumentBecomesVisible() {
if (document.visibilityState === "visible") {
await retry();
}
if (document.visibilityState === "visible") {
await retry();
}
}