a fork of iceshrimp.net but a tweaked frontend to my personal liking. waow
fediverse social-media social iceshrimp fedi
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[backend/razor] Add a navigation bar to the admin dashboard

This is implemented using a reusable navigation bar component.

+352 -19
+1
Iceshrimp.Backend/Components/Admin/AdminHead.razor
··· 2 2 @using Iceshrimp.Backend.Components.Helpers 3 3 <HeadContent> 4 4 <VersionedLink rel="stylesheet" href="/css/admin.css"/> 5 + <VersionedLink rel="stylesheet" href="/_content/Iceshrimp.Assets.PhosphorIcons/css/ph-regular.css"/> 5 6 <VersionedScript src="/js/admin.js"/> 6 7 </HeadContent>
+20
Iceshrimp.Backend/Components/Admin/AdminNav.razor
··· 1 + @using Iceshrimp.Assets.PhosphorIcons 2 + @using Iceshrimp.Backend.Components.Generic 3 + <NavBar Brand="_brand" Links="_links" Right="_right" MaxItemsLg="7" MaxItemsMd="3"/> 4 + 5 + @code 6 + { 7 + private NavBar.NavLink _brand = new("/admin", "Admin Dashboard"); 8 + 9 + private List<NavBar.NavLink> _links = 10 + [ 11 + new("/admin", "Overview", Icons.ChartLine), // spacer for alignment 12 + new("/admin/metadata", "Instance metadata", Icons.Info), 13 + new("/admin/users", "User management", Icons.Users), 14 + new("/admin/federation", "Federation control", Icons.Graph), 15 + new("/admin/relays", "Relays", Icons.FastForward), 16 + new("/admin/plugins", "Plugins", Icons.Plug) 17 + ]; 18 + 19 + private List<NavBar.NavLink> _right = [new("/queue", "Queue dashboard", IconRight: Icons.ArrowSquareOut, NewTab: true)]; 20 + }
+2 -2
Iceshrimp.Backend/Components/Admin/AdminPageHeader.razor
··· 1 1 @using Microsoft.AspNetCore.Components.Web 2 2 <PageTitle>@Title - Admin - @(InstanceName ?? "Iceshrimp.NET")</PageTitle> 3 - <h1>Admin Dashboard</h1> 4 - <button role="link" data-target="/admin" onclick="navigate(event)">Return to overview</button> 3 + <AdminHead/> 4 + <AdminNav/> 5 5 <h2>@Title</h2> 6 6 7 7 @code {
+147
Iceshrimp.Backend/Components/Generic/NavBar.razor
··· 1 + @using Iceshrimp.Assets.PhosphorIcons 2 + @using Iceshrimp.Backend.Components.Helpers 3 + <nav class="navbar navbar-lg"> 4 + <ul> 5 + <li> 6 + <a href="@Brand.Href" class="brand">@Brand.Name</a> 7 + </li> 8 + </ul> 9 + <ul> 10 + @foreach (var link in Links[..Math.Min(MaxItemsLg, Links.Count)]) 11 + { 12 + <li> 13 + <NavBarLink Link="link"/> 14 + </li> 15 + } 16 + @if (OverflowsLg) 17 + { 18 + var offset = Links.Count - MaxItemsLg; 19 + <li class="dropdown"> 20 + <a class="dropdown-button" tabindex="0"> 21 + <Icon Name="Icons.DotsThree" Size="20pt"/> 22 + </a> 23 + <ul class="dropdown-menu"> 24 + @foreach (var link in Links[offset..]) 25 + { 26 + <li> 27 + <NavBarLink Link="link"/> 28 + </li> 29 + } 30 + @if (Right is { Count: > 0 }) 31 + { 32 + <li class="dropdown-spacer"></li> 33 + @foreach (var link in Right) 34 + { 35 + <li> 36 + <NavBarLink Link="link"/> 37 + </li> 38 + } 39 + } 40 + </ul> 41 + </li> 42 + } 43 + </ul> 44 + @if (!OverflowsLg) 45 + { 46 + <ul class="nav-right"> 47 + @if (Right is { Count: > 0 }) 48 + { 49 + foreach (var link in Right) 50 + { 51 + <li> 52 + <NavBarLink Link="link"/> 53 + </li> 54 + } 55 + } 56 + </ul> 57 + } 58 + </nav> 59 + <nav class="navbar navbar-md"> 60 + <ul> 61 + <li> 62 + <a href="@Brand.Href" class="brand">@Brand.Name</a> 63 + </li> 64 + </ul> 65 + <ul> 66 + @foreach (var link in Links[..Math.Min(MaxItemsMd, Links.Count)]) 67 + { 68 + <li> 69 + <NavBarLink Link="link"/> 70 + </li> 71 + } 72 + @if (OverflowsMd) 73 + { 74 + var offset = Links.Count - MaxItemsMd; 75 + <li class="dropdown"> 76 + <a class="dropdown-button" tabindex="0"> 77 + <Icon Name="Icons.DotsThree" Size="20pt"/> 78 + </a> 79 + <ul class="dropdown-menu"> 80 + @foreach (var link in Links[offset..]) 81 + { 82 + <li> 83 + <NavBarLink Link="link"/> 84 + </li> 85 + } 86 + @if (Right is { Count: > 0 }) 87 + { 88 + <li class="dropdown-spacer"></li> 89 + @foreach (var link in Right) 90 + { 91 + <li> 92 + <NavBarLink Link="link"/> 93 + </li> 94 + } 95 + } 96 + </ul> 97 + </li> 98 + } 99 + </ul> 100 + </nav> 101 + 102 + <nav class="navbar navbar-sm"> 103 + <ul> 104 + <li> 105 + <a href="@Brand.Href" class="brand">@Brand.Name</a> 106 + </li> 107 + </ul> 108 + <ul class="nav-right"> 109 + <li> 110 + <a class="hamburger-button" href="#" onclick="toggleHamburger(event)"> 111 + <Icon Name="Icons.List" Size="20pt"/> 112 + </a> 113 + <ul class="hamburger-menu hidden"> 114 + @foreach (var link in Links) 115 + { 116 + <li> 117 + <NavBarLink Link="link"/> 118 + </li> 119 + } 120 + @if (Right is { Count: > 0 }) 121 + { 122 + <li class="hamburger-spacer"></li> 123 + @foreach (var link in Right) 124 + { 125 + <li> 126 + <NavBarLink Link="link"/> 127 + </li> 128 + } 129 + } 130 + </ul> 131 + </li> 132 + </ul> 133 + </nav> 134 + <VersionedScript src="/Components/Generic/NavBar.razor.js"/> 135 + 136 + @code { 137 + public record struct NavLink(string Href, string Name, IconName? Icon = null, IconName? IconRight = null, bool NewTab = false); 138 + 139 + [Parameter, EditorRequired] public required int MaxItemsLg { get; set; } 140 + [Parameter, EditorRequired] public required int MaxItemsMd { get; set; } 141 + [Parameter, EditorRequired] public required NavLink Brand { get; set; } 142 + [Parameter, EditorRequired] public required List<NavLink> Links { get; set; } 143 + [Parameter] public List<NavLink>? Right { get; set; } 144 + 145 + private bool OverflowsLg => Links.Count + (Right?.Count ?? 0) > MaxItemsLg; 146 + private bool OverflowsMd => Links.Count + (Right?.Count ?? 0) > MaxItemsMd; 147 + }
+146
Iceshrimp.Backend/Components/Generic/NavBar.razor.css
··· 1 + .navbar { 2 + position: absolute; 3 + left: 0; 4 + top: 0; 5 + right: 0; 6 + height: 50px; 7 + padding: 0; 8 + background-color: var(--button-base); 9 + display: flex; 10 + align-items: center; 11 + white-space: nowrap; 12 + vertical-align: middle; 13 + } 14 + 15 + a { 16 + text-decoration: none; 17 + } 18 + 19 + .navbar ::deep a { 20 + color: var(--text-main); 21 + } 22 + 23 + .navbar ul { 24 + margin: 0; 25 + padding: 0; 26 + height: 100%; 27 + list-style-type: none; 28 + display: flex; 29 + align-items: center; 30 + } 31 + 32 + .navbar .brand { 33 + color: var(--text-bright); 34 + padding: 0 15px; 35 + font-weight: bold; 36 + } 37 + 38 + .navbar ul.nav-right:last-of-type li:last-of-type ::deep a { 39 + padding: 0 15px; 40 + } 41 + 42 + .navbar ul li { 43 + height: 100%; 44 + } 45 + 46 + .navbar ul li ::deep a { 47 + color: var(--text-main); 48 + padding: 0 12px; 49 + height: 100%; 50 + display: flex; 51 + align-items: center; 52 + justify-content: flex-start; 53 + gap: 4pt; 54 + } 55 + 56 + .navbar ::deep a.active, 57 + .navbar ul ::deep a:hover, 58 + .navbar ul ::deep a:focus, 59 + .navbar .brand:hover, 60 + .navbar .brand:focus { 61 + background-color: var(--button-hover); 62 + text-decoration: none; 63 + } 64 + 65 + .navbar ul.nav-right { 66 + margin-left: auto; 67 + } 68 + 69 + .dropdown-button { 70 + cursor: pointer; 71 + } 72 + 73 + .dropdown:hover > .dropdown-menu, 74 + .dropdown:focus-within > .dropdown-menu, 75 + .dropdown-menu:hover, 76 + .dropdown-menu:focus { 77 + visibility: visible; 78 + opacity: 1; 79 + display: block !important; 80 + } 81 + 82 + .dropdown { 83 + position: relative; 84 + } 85 + 86 + .dropdown-menu { 87 + opacity: 0; 88 + min-width: 5rem; 89 + position: absolute; 90 + margin-top: 1rem; 91 + left: 0; 92 + z-index: +1; 93 + display: none !important; 94 + } 95 + 96 + ul.dropdown-menu li { 97 + background-color: var(--button-base); 98 + } 99 + 100 + li.dropdown-spacer, 101 + li.hamburger-spacer { 102 + height: 1px !important; 103 + border-top: 1px solid var(--border); 104 + filter: brightness(65%); 105 + } 106 + 107 + .hamburger-button { 108 + cursor: pointer; 109 + } 110 + 111 + .hamburger-menu.hidden { 112 + display: none !important; 113 + } 114 + 115 + .hamburger-menu { 116 + position: absolute; 117 + right: 0; 118 + display: inline-block !important; 119 + z-index: +1; 120 + } 121 + 122 + ul.hamburger-menu li { 123 + display: block !important; 124 + background-color: var(--button-base); 125 + } 126 + 127 + .navbar-lg { 128 + display: none; 129 + @media screen and (min-width: 1200px) { 130 + display: flex; 131 + } 132 + } 133 + 134 + .navbar-md { 135 + display: none; 136 + @media screen and (min-width: 800px) and (max-width: 1199px) { 137 + display: flex; 138 + } 139 + } 140 + 141 + .navbar-sm { 142 + display: none; 143 + @media screen and (max-width: 799px) { 144 + display: flex; 145 + } 146 + }
+11
Iceshrimp.Backend/Components/Generic/NavBar.razor.js
··· 1 + function toggleHamburger(e) { 2 + e.preventDefault() 3 + 4 + for (let el of document.getElementsByClassName("hamburger-menu")) { 5 + if (el.classList.contains("hidden")) { 6 + el.classList.remove('hidden'); 7 + } else { 8 + el.classList.add('hidden'); 9 + } 10 + } 11 + }
+18
Iceshrimp.Backend/Components/Generic/NavBarLink.razor
··· 1 + @using Microsoft.AspNetCore.Components.Routing 2 + @using Iceshrimp.Assets.PhosphorIcons 3 + <NavLink href="@Link.Href" class="nav-link" Match="NavLinkMatch.All" target="@Target"> 4 + @if (Link.Icon != null) 5 + { 6 + <Icon Name="Link.Icon"/> 7 + } 8 + @Link.Name 9 + @if (Link.IconRight != null) 10 + { 11 + <Icon Name="Link.IconRight"/> 12 + } 13 + </NavLink> 14 + 15 + @code { 16 + [Parameter, EditorRequired] public required NavBar.NavLink Link { get; set; } 17 + private string Target => Link.NewTab ? "_blank" : "_self"; 18 + }
-1
Iceshrimp.Backend/Pages/Admin/Federation.razor
··· 9 9 @using static Iceshrimp.Backend.Core.Configuration.Enums; 10 10 @using Microsoft.AspNetCore.Components.Forms 11 11 @inherits AdminComponentBase 12 - <AdminHead/> 13 12 <AdminPageHeader Title="@($"{ModeString} instances")"/> 14 13 <table> 15 14 <thead>
-1
Iceshrimp.Backend/Pages/Admin/Metadata.razor
··· 5 5 @using Microsoft.AspNetCore.Components.Forms 6 6 @inherits AdminComponentBase 7 7 8 - <AdminHead/> 9 8 <AdminPageHeader Title="Instance metadata"/> 10 9 11 10 <p>Here you can adjust basic instance metadata. It gets displayed to all users, including guests.</p>
+2 -12
Iceshrimp.Backend/Pages/Admin/Overview.razor
··· 4 4 @using Microsoft.EntityFrameworkCore 5 5 @inherits AdminComponentBase 6 6 7 - <PageTitle>Overview - Admin - @(InstanceName ?? "Iceshrimp.NET")</PageTitle> 8 - <AdminHead/> 9 - <h1>Admin Dashboard</h1> 10 - <p>This interface is used to adjust parameters of this Iceshrimp.NET instance. Please pick a category below.</p> 11 - 12 - <button role="link" data-target="/admin/metadata" onclick="navigate(event)">Instance metadata</button> 13 - <button role="link" data-target="/admin/users" onclick="navigate(event)">User management</button> 14 - <button role="link" data-target="/admin/federation" onclick="navigate(event)">Federation control</button> 15 - <button role="link" data-target="/admin/relays" onclick="navigate(event)">Relays</button> 16 - <button role="link" data-target="/admin/plugins" onclick="navigate(event)">Plugins</button> 17 - <button role="link" data-target="/queue" onclick="navigate(event)">[ext] Queue dashboard</button> 7 + <AdminPageHeader Title="Overview"/> 8 + <p>This interface is used to adjust parameters of this Iceshrimp.NET instance.</p> 18 9 19 10 @* 20 - - TODO: Improve styles, possibly add a navbar 21 11 - TODO: Remote user management 22 12 - TODO: More federation stats (e.g. # of activities sent/fetched, some basic queue stats) 23 13 - TODO: Move queue dashboard to blazor ssr
-1
Iceshrimp.Backend/Pages/Admin/Plugins.razor
··· 2 2 @using Iceshrimp.Backend.Components.Admin 3 3 @using Iceshrimp.Backend.Core.Helpers 4 4 @inherits AdminComponentBase 5 - <AdminHead/> 6 5 <AdminPageHeader Title="Plugins"/> 7 6 8 7 <table>
-1
Iceshrimp.Backend/Pages/Admin/Relays.razor
··· 9 9 @using Microsoft.Extensions.Options 10 10 @using Microsoft.AspNetCore.Components.Forms 11 11 @inherits AdminComponentBase 12 - <AdminHead/> 13 12 <AdminPageHeader Title="Relays"/> 14 13 15 14 @if (!Options.Value.AcceptLdSignatures)
-1
Iceshrimp.Backend/Pages/Admin/Users.razor
··· 5 5 @using Microsoft.EntityFrameworkCore 6 6 @using Microsoft.Extensions.Options 7 7 @inherits AdminComponentBase 8 - <AdminHead/> 9 8 <AdminPageHeader Title="Users"/> 10 9 11 10 <table>
+1
Iceshrimp.Backend/Pages/Shared/RootComponent.razor
··· 14 14 <!--suppress HtmlRequiredTitleElement, Justification: HeadOutlet --> 15 15 <head> 16 16 <meta charset="utf-8"/> 17 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 17 18 <VersionedLink rel="stylesheet" href="/Iceshrimp.Backend.styles.css"/> 18 19 <VersionedLink rel="stylesheet" href="/css/default.css"/> 19 20 <VersionedLink rel="icon" type="image/png" href="/favicon.png"/>
+4
Iceshrimp.Backend/wwwroot/css/admin.css
··· 13 13 14 14 .width30 { 15 15 width: 30ch; 16 + } 17 + 18 + body { 19 + margin-top: 60px; 16 20 }