mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-07 14:56:02 -04:00
Replace bootstrap with tailwind for admin (#113)
This commit is contained in:
20
src/AliasVault.Admin2/Account/Pages/Logout.razor
Normal file
20
src/AliasVault.Admin2/Account/Pages/Logout.razor
Normal file
@@ -0,0 +1,20 @@
|
||||
@page "/user/logout"
|
||||
@using AliasVault.Admin2.Services
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@inject SignInManager<IdentityUser> SignInManager
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject GlobalNotificationService GlobalNotificationService
|
||||
|
||||
@code {
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
// Sign out the user
|
||||
await SignInManager.SignOutAsync();
|
||||
GlobalNotificationService.ClearMessages();
|
||||
|
||||
// Redirect to the home page with hard refresh.
|
||||
NavigationManager.NavigateTo("/", true);
|
||||
}
|
||||
}
|
||||
@@ -25,59 +25,6 @@
|
||||
<ProjectReference Include="..\Databases\AliasServerDb\AliasServerDb.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\AccessDenied.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ConfirmEmail.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ConfirmEmailChange.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ExternalLogin.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ForgotPassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ForgotPasswordConfirmation.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\InvalidPasswordReset.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\InvalidUser.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Lockout.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Login.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\LoginWith2fa.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\LoginWithRecoveryCode.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\ChangePassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\DeletePersonalData.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\Disable2fa.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\Email.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\EnableAuthenticator.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\ExternalLogins.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\GenerateRecoveryCodes.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\Index.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\PersonalData.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\ResetAuthenticator.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\SetPassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\TwoFactorAuthentication.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Manage\_Imports.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\Register.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\RegisterConfirmation.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ResendEmailConfirmation.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ResetPassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\ResetPasswordConfirmation.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Pages\_Imports.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\AccountLayout.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\ExternalLoginPicker.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\ManageLayout.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\ManageNavMenu.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\RedirectToLogin.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\ShowRecoveryCodes.razor" />
|
||||
<_ContentIncludedByDefault Remove="Components\Account\Shared\StatusMessage.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\ChangePassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\DeletePersonalData.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\Disable2fa.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\Email.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\EnableAuthenticator.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\GenerateRecoveryCodes.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\Index.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\PersonalData.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\ResetAuthenticator.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\SetPassword.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\TwoFactorAuthentication.razor" />
|
||||
<_ContentIncludedByDefault Remove="Account\Pages\Manage\_Imports.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Account\Pages\AccessDenied.razor" />
|
||||
<AdditionalFiles Include="Account\Pages\ConfirmEmail.razor" />
|
||||
@@ -98,9 +45,6 @@
|
||||
<AdditionalFiles Include="Account\Pages\ResetPasswordConfirmation.razor" />
|
||||
<AdditionalFiles Include="Account\Pages\_Imports.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\AuthLayout.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\ExternalLoginPicker.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\ManageLayout.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\ManageNavMenu.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\RedirectToLogin.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\ShowRecoveryCodes.razor" />
|
||||
<AdditionalFiles Include="Account\Shared\StatusMessage.razor" />
|
||||
|
||||
@@ -5,15 +5,54 @@
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<base href="/"/>
|
||||
<link rel="stylesheet" href="bootstrap/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="app.css"/>
|
||||
<link rel="stylesheet" href="css/tailwind.css"/>
|
||||
<link rel="stylesheet" href="css/app.css"/>
|
||||
<link rel="stylesheet" href="AliasVault.Admin2.styles.css"/>
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<HeadOutlet @rendermode="RenderModeForPage"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="bg-gray-50 dark:bg-gray-800">
|
||||
<Routes @rendermode="RenderModeForPage"/>
|
||||
<script src="js/dark-mode.js?v=dev"></script>
|
||||
<script src="js/utilities.js?v=dev"></script>
|
||||
|
||||
<script>
|
||||
window.initTopMenu = function() {
|
||||
initDarkModeSwitcher();
|
||||
};
|
||||
|
||||
window.registerClickOutsideHandler = (dotNetHelper) => {
|
||||
document.addEventListener('click', (event) => {
|
||||
const menu = document.getElementById('userMenuDropdown');
|
||||
const menuButton = document.getElementById('userMenuDropdownButton');
|
||||
if (menu && !menu.contains(event.target) && !menuButton.contains(event.target)) {
|
||||
dotNetHelper.invokeMethodAsync('CloseMenu');
|
||||
}
|
||||
|
||||
const mobileMenu = document.getElementById('mobileMenu');
|
||||
const mobileMenuButton = document.getElementById('toggleMobileMenuButton');
|
||||
if (mobileMenu && !mobileMenu.contains(event.target) && !mobileMenuButton.contains(event.target)) {
|
||||
dotNetHelper.invokeMethodAsync('CloseMenu');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.clipboardCopy = {
|
||||
copyText: function (text) {
|
||||
navigator.clipboard.writeText(text).then(function () { })
|
||||
.catch(function (error) {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Primarily used by E2E tests.
|
||||
window.blazorNavigate = (url) => {
|
||||
Blazor.navigateTo(url);
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
10
src/AliasVault.Admin2/Main/Layout/Footer.razor
Normal file
10
src/AliasVault.Admin2/Main/Layout/Footer.razor
Normal file
@@ -0,0 +1,10 @@
|
||||
<footer class="md:flex md:items-center md:justify-between px-4 2xl:px-0 py-6 md:py-10">
|
||||
<p class="text-sm text-center text-gray-500 mb-4 md:mb-0">
|
||||
© 2024 AliasVault. All rights reserved.
|
||||
</p>
|
||||
<ul class="flex flex-wrap items-center justify-center">
|
||||
<li><a href="#" class="mr-4 text-sm font-normal text-gray-500 hover:underline md:mr-6 dark:text-gray-400">Terms and conditions</a></li>
|
||||
<li><a href="#" class="mr-4 text-sm font-normal text-gray-500 hover:underline md:mr-6 dark:text-gray-400">License</a></li>
|
||||
<li><a href="https://github.com/lanedirt/AliasVault" target="_blank" class="text-sm font-normal text-gray-500 hover:underline dark:text-gray-400">GitHub</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
83
src/AliasVault.Admin2/Main/Layout/Footer.razor.css
Normal file
83
src/AliasVault.Admin2/Main/Layout/Footer.razor.css
Normal file
@@ -0,0 +1,83 @@
|
||||
.navbar-toggler {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.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;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a {
|
||||
color: #d7d7d7;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -2,37 +2,14 @@
|
||||
@implements IDisposable
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu/>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="account/manage">
|
||||
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> @context.User.Identity?.Name
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<form action="Account/Logout" method="post">
|
||||
<AntiforgeryToken/>
|
||||
<input type="hidden" name="ReturnUrl" value="@currentUrl"/>
|
||||
<button type="submit" class="nav-link">
|
||||
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
<TopMenu />
|
||||
<div class="flex pt-16 overflow-hidden bg-gray-50 dark:bg-gray-900">
|
||||
<div id="main-content" class="relative w-full max-w-screen-2xl mx-auto h-full overflow-y-auto bg-gray-50 dark:bg-gray-900">
|
||||
<main>
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">AliasVault Admin</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler"/>
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<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> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="emails" Match="NavLinkMatch.Prefix">
|
||||
<span class="bi bi-lock-nav-menu" aria-hidden="true"></span> Emails
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -1,125 +0,0 @@
|
||||
.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;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-lock-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath d='M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-person-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person' viewBox='0 0 16 16'%3E%3Cpath d='M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-person-badge-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-badge' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z'/%3E%3Cpath d='M4.5 0A2.5 2.5 0 0 0 2 2.5V14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2.5A2.5 2.5 0 0 0 11.5 0h-7zM3 2.5A1.5 1.5 0 0 1 4.5 1h7A1.5 1.5 0 0 1 13 2.5v10.795a4.2 4.2 0 0 0-.776-.492C11.392 12.387 10.063 12 8 12s-3.392.387-4.224.803a4.2 4.2 0 0 0-.776.492V2.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-person-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-fill' viewBox='0 0 16 16'%3E%3Cpath d='M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-arrow-bar-left-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-arrow-bar-left' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
142
src/AliasVault.Admin2/Main/Layout/TopMenu.razor
Normal file
142
src/AliasVault.Admin2/Main/Layout/TopMenu.razor
Normal file
@@ -0,0 +1,142 @@
|
||||
@inherits MainBase
|
||||
@implements IDisposable
|
||||
|
||||
<header>
|
||||
<nav class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700 py-3 px-4 bg-primary-100">
|
||||
<div class="flex justify-between items-center max-w-screen-2xl mx-auto">
|
||||
<div class="flex justify-start items-center">
|
||||
<a href="/" class="flex mr-14">
|
||||
<img src="/icon-trimmed.png" class="mr-3 h-8" alt="AliasVault Logo">
|
||||
<span class="self-center hidden sm:flex text-2xl font-semibold whitespace-nowrap dark:text-white">AliasVault</span>
|
||||
<span class="ps-2 self-center hidden sm:flex text-2xl font-black whitespace-nowrap dark:text-white">Admin</span>
|
||||
</a>
|
||||
|
||||
<div class="hidden justify-between items-center w-full lg:flex lg:w-auto lg:order-1">
|
||||
<ul class="flex flex-col mt-4 space-x-6 text-sm font-medium lg:flex-row xl:space-x-8 lg:mt-0">
|
||||
<NavLink href="/emails" class="block text-gray-700 hover:text-primary-700 dark:text-gray-400 dark:hover:text-white" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Emails
|
||||
</NavLink>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end items-center lg:order-2">
|
||||
<button id="theme-toggle" data-tooltip-target="tooltip-toggle" type="button" class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5">
|
||||
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
|
||||
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
||||
</button>
|
||||
<div id="tooltip-toggle" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip" data-popper-placement="bottom" style="position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate3d(1377px, 60px, 0px);">
|
||||
Toggle dark mode
|
||||
<div class="tooltip-arrow" data-popper-arrow="" style="position: absolute; left: 0px; transform: translate3d(68.5px, 0px, 0px);"></div>
|
||||
</div>
|
||||
|
||||
<button @onclick="ToggleMenu" type="button" class="flex mx-3 text-sm bg-gray-800 rounded-full md:mr-0 flex-shrink-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600" id="userMenuDropdownButton" aria-expanded="false" data-dropdown-toggle="userMenuDropdown">
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<img class="w-8 h-8 rounded-full" src="/img/avatar.webp" alt="user photo">
|
||||
</button>
|
||||
|
||||
@if (isMenuOpen)
|
||||
{
|
||||
<div class="absolute top-10 right-0 z-50 my-4 w-56 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700 dark:divide-gray-600" id="userMenuDropdown" data-popper-placement="bottom">
|
||||
<div class="py-3 px-4">
|
||||
<span class="block text-sm font-semibold text-gray-900 dark:text-white">@_username</span>
|
||||
</div>
|
||||
<ul class="py-1 font-light text-gray-500 dark:text-gray-400" aria-labelledby="userMenuDropdownButton">
|
||||
<li>
|
||||
<a href="account/manage" class="block py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400 dark:hover:text-white">Account settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="py-1 font-light text-gray-500 dark:text-gray-400" aria-labelledby="dropdown">
|
||||
<li>
|
||||
<a href="/user/logout" class="block py-2 px-4 font-bold text-sm text-primary-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-primary-200 dark:hover:text-white">Sign out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
<button @onclick="ToggleMobileMenu" type="button" id="toggleMobileMenuButton" class="items-center p-2 text-gray-500 rounded-lg md:ml-2 lg:hidden hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
|
||||
<span class="sr-only">Open menu</span>
|
||||
<svg class="w-6 h-6" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@if (isMobileMenuOpen)
|
||||
{
|
||||
<nav class="bg-white dark:bg-gray-900">
|
||||
<ul id="mobileMenu" class="flex-col mt-0 pt-16 w-full text-sm font-medium lg:hidden">
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<NavLink href="/" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Home
|
||||
</NavLink>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<NavLink href="/credentials" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Credentials
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
</header>
|
||||
|
||||
@code {
|
||||
private bool isMenuOpen = false;
|
||||
private bool isMobileMenuOpen = false;
|
||||
private string _username { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Close the menu.
|
||||
/// </summary>
|
||||
[JSInvokable]
|
||||
public void CloseMenu()
|
||||
{
|
||||
isMenuOpen = false;
|
||||
isMobileMenuOpen = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose method.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
NavigationManager.LocationChanged -= LocationChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
_username = GetUsername();
|
||||
NavigationManager.LocationChanged += LocationChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (firstRender)
|
||||
{
|
||||
await Js.InvokeVoidAsync("window.initTopMenu");
|
||||
DotNetObjectReference<TopMenu> objRef = DotNetObjectReference.Create(this);
|
||||
await Js.InvokeVoidAsync("window.registerClickOutsideHandler", objRef);
|
||||
}
|
||||
}
|
||||
|
||||
private void LocationChanged(object? sender, LocationChangedEventArgs e)
|
||||
{
|
||||
isMenuOpen = false;
|
||||
isMobileMenuOpen = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void ToggleMenu()
|
||||
{
|
||||
isMenuOpen = !isMenuOpen;
|
||||
}
|
||||
|
||||
private void ToggleMobileMenu()
|
||||
{
|
||||
isMobileMenuOpen = !isMobileMenuOpen;
|
||||
}
|
||||
}
|
||||
83
src/AliasVault.Admin2/Main/Layout/TopMenu.razor.css
Normal file
83
src/AliasVault.Admin2/Main/Layout/TopMenu.razor.css
Normal file
@@ -0,0 +1,83 @@
|
||||
.navbar-toggler {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.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;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a {
|
||||
color: #d7d7d7;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -11,23 +11,22 @@
|
||||
<PageTitle>Profile</PageTitle>
|
||||
|
||||
<h3>Profile</h3>
|
||||
<StatusMessage/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="flex flex-wrap -mx-3">
|
||||
<div class="w-full md:w-1/2 px-3">
|
||||
<EditForm Model="Input" FormName="profile" OnValidSubmit="OnValidSubmitAsync">
|
||||
<DataAnnotationsValidator/>
|
||||
<ValidationSummary class="text-danger" role="alert"/>
|
||||
<div class="form-floating mb-3">
|
||||
<input type="text" value="@username" class="form-control" placeholder="Please choose your username." disabled/>
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<ValidationSummary class="text-red-500 mb-4" role="alert"/>
|
||||
<div class="mb-4">
|
||||
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
|
||||
<input type="text" value="@username" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-gray-100 cursor-not-allowed" placeholder="Please choose your username." disabled/>
|
||||
</div>
|
||||
<div class="form-floating mb-3">
|
||||
<InputText @bind-Value="Input.PhoneNumber" class="form-control" placeholder="Please enter your phone number."/>
|
||||
<label for="phone-number" class="form-label">Phone number</label>
|
||||
<ValidationMessage For="() => Input.PhoneNumber" class="text-danger"/>
|
||||
<div class="mb-4">
|
||||
<label for="phone-number" class="block text-sm font-medium text-gray-700 mb-1">Phone number</label>
|
||||
<InputText @bind-Value="Input.PhoneNumber" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500" placeholder="Please enter your phone number."/>
|
||||
<ValidationMessage For="() => Input.PhoneNumber" class="text-red-500 text-sm mt-1"/>
|
||||
</div>
|
||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
|
||||
<button type="submit" class="w-full bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-md transition duration-150 ease-in-out">Save</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,24 @@
|
||||
@using AliasVault.Admin2.Main.Layout
|
||||
@layout MainLayout
|
||||
|
||||
<h1>Manage your account</h1>
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<GlobalNotificationDisplay />
|
||||
|
||||
<div>
|
||||
<h2>Change your account settings</h2>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Manage account</h1>
|
||||
</div>
|
||||
<p>Manage your profile here.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<hr class="mb-6 border-t border-gray-300"/>
|
||||
<div class="flex flex-col md:flex-row">
|
||||
<div class="w-full md:w-1/4 mb-6 md:mb-0">
|
||||
<ManageNavMenu/>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="w-full md:w-3/4 md:pl-8">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,27 +2,27 @@
|
||||
|
||||
@inject SignInManager<AdminUser> SignInManager
|
||||
|
||||
<ul class="nav nav-pills flex-column">
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage" Match="NavLinkMatch.All">Profile</NavLink>
|
||||
<ul class="flex flex-col space-y-1">
|
||||
<li>
|
||||
<NavLink href="account/manage" Match="NavLinkMatch.All" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">Profile</NavLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage/Email">Email</NavLink>
|
||||
<li>
|
||||
<NavLink href="account/manage/Email" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">Email</NavLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage/ChangePassword">Password</NavLink>
|
||||
<li>
|
||||
<NavLink href="account/manage/ChangePassword" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">Password</NavLink>
|
||||
</li>
|
||||
@if (hasExternalLogins)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage/ExternalLogins">External logins</NavLink>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage/TwoFactorAuthentication">Two-factor authentication</NavLink>
|
||||
<li>
|
||||
<NavLink href="account/manage/ExternalLogins" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">External logins</NavLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="account/manage/PersonalData">Personal data</NavLink>
|
||||
}
|
||||
<li>
|
||||
<NavLink href="account/manage/TwoFactorAuthentication" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">Two-factor authentication</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink href="account/manage/PersonalData" class="block px-4 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-100 hover:text-gray-900 transition-colors duration-150">Personal data</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
<PageTitle>Emails</PageTitle>
|
||||
|
||||
<h1>Emails</h1>
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Emails</h1>
|
||||
</div>
|
||||
<p>This page gives an overview of recently received mails by this AliasVault server.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
@@ -12,14 +20,44 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-4">
|
||||
@foreach (var email in EmailList)
|
||||
{
|
||||
<div>
|
||||
<div>@email.From</div>
|
||||
<div>@email.MessagePreview</div>
|
||||
</div>
|
||||
}
|
||||
<div class="overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<table class="w-full text-sm text-left text-gray-500">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-4 py-3">ID</th>
|
||||
<th scope="col" class="px-4 py-3">Subject</th>
|
||||
<th scope="col" class="px-4 py-3">From</th>
|
||||
<th scope="col" class="px-4 py-3">To</th>
|
||||
<th scope="col" class="px-4 py-3">Date</th>
|
||||
<th scope="col" class="px-4 py-3">System Date</th>
|
||||
<th scope="col" class="px-4 py-3">Preview</th>
|
||||
<th scope="col" class="px-4 py-3">Attachments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var email in EmailList)
|
||||
{
|
||||
<tr class="bg-white border-b hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-medium text-gray-900">@email.Id</td>
|
||||
<td class="px-4 py-3">@email.Subject</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="font-medium">@email.FromLocal</span>@@@email.FromDomain
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="font-medium">@email.ToLocal</span>@@@email.ToDomain
|
||||
</td>
|
||||
<td class="px-4 py-3">@email.Date.ToString("yyyy-MM-dd HH:mm")</td>
|
||||
<td class="px-4 py-3">@email.DateSystem.ToString("yyyy-MM-dd HH:mm")</td>
|
||||
<td class="px-4 py-3">
|
||||
<span class="line-clamp-1">@email.MessagePreview</span>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
@email.Attachments.Count
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -43,6 +81,7 @@ else
|
||||
// Load the aliases from the database.
|
||||
var user = UserService.User();
|
||||
EmailList = await DbContext.Emails
|
||||
.OrderByDescending(x => x.DateSystem)
|
||||
.ToListAsync();
|
||||
|
||||
IsLoading = false;
|
||||
|
||||
@@ -3,8 +3,13 @@
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
<h1>AliasVault Admin</h1>
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">AliasVault Admin</h1>
|
||||
</div>
|
||||
<p>Welcome to the AliasVault admin portal.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems"/>
|
||||
|
||||
Welcome to the AliasVault admin portal.
|
||||
|
||||
@@ -118,4 +118,13 @@ public class MainBase : OwningComponentBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username from the authentication state asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>The username.</returns>
|
||||
protected string GetUsername()
|
||||
{
|
||||
return UserService.User().UserName ?? "[Unknown]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
@using AliasVault.Admin2.Main.Components.Alerts
|
||||
@using AliasVault.Admin2.Main.Components.Layout
|
||||
@using AliasVault.Admin2.Main.Models
|
||||
@using AliasVault.Admin2.Main.Pages
|
||||
@using AliasVault.Admin2.Services
|
||||
@using AliasServerDb
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
|
||||
1425
src/AliasVault.Admin2/package-lock.json
generated
Normal file
1425
src/AliasVault.Admin2/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
src/AliasVault.Admin2/package.json
Normal file
18
src/AliasVault.Admin2/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "aliasvault.client",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build:css": "tailwindcss -i ./tailwind.css -o ./wwwroot/css/tailwind.css --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.3"
|
||||
}
|
||||
}
|
||||
6
src/AliasVault.Admin2/postcss.config.js
Normal file
6
src/AliasVault.Admin2/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
57
src/AliasVault.Admin2/tailwind.config.js
Normal file
57
src/AliasVault.Admin2/tailwind.config.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./**/*.html',
|
||||
'./**/*.razor',
|
||||
],
|
||||
safelist: [
|
||||
'w-64',
|
||||
'w-1/2',
|
||||
'rounded-l-lg',
|
||||
'rounded-r-lg',
|
||||
'bg-gray-200',
|
||||
'grid-cols-4',
|
||||
'grid-cols-7',
|
||||
'h-6',
|
||||
'leading-6',
|
||||
'h-9',
|
||||
'leading-9',
|
||||
'shadow-lg',
|
||||
'bg-opacity-50',
|
||||
'dark:bg-opacity-80'
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
"900": "#7b4a1e",
|
||||
"800": "#9a5d26",
|
||||
"700": "#b8702f",
|
||||
"600": "#d68338",
|
||||
"500": "#f49541",
|
||||
"400": "#f6a752",
|
||||
"300": "#f8b963",
|
||||
"200": "#fbcb74",
|
||||
"100": "#fdde85",
|
||||
"50": "#ffe096"
|
||||
}
|
||||
|
||||
},
|
||||
fontFamily: {
|
||||
'sans': ['Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'system-ui', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', 'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'],
|
||||
'body': ['Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'system-ui', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', 'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'],
|
||||
'mono': ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', 'monospace']
|
||||
},
|
||||
transitionProperty: {
|
||||
'width': 'width'
|
||||
},
|
||||
textDecoration: ['active'],
|
||||
minWidth: {
|
||||
'kanban': '28rem'
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
],
|
||||
}
|
||||
3
src/AliasVault.Admin2/tailwind.css
Normal file
3
src/AliasVault.Admin2/tailwind.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,16 +2,6 @@ html, body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a, .btn-link {
|
||||
color: #006bb7;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
||||
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
||||
}
|
||||
1638
src/AliasVault.Admin2/wwwroot/css/tailwind.css
Normal file
1638
src/AliasVault.Admin2/wwwroot/css/tailwind.css
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 936 B |
BIN
src/AliasVault.Admin2/wwwroot/horizontal-logo-cropped.png
Normal file
BIN
src/AliasVault.Admin2/wwwroot/horizontal-logo-cropped.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
src/AliasVault.Admin2/wwwroot/icon-trimmed.png
Normal file
BIN
src/AliasVault.Admin2/wwwroot/icon-trimmed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/AliasVault.Admin2/wwwroot/img/avatar.webp
Normal file
BIN
src/AliasVault.Admin2/wwwroot/img/avatar.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 KiB |
BIN
src/AliasVault.Admin2/wwwroot/img/service-placeholder.webp
Normal file
BIN
src/AliasVault.Admin2/wwwroot/img/service-placeholder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
53
src/AliasVault.Admin2/wwwroot/js/dark-mode.js
Normal file
53
src/AliasVault.Admin2/wwwroot/js/dark-mode.js
Normal file
@@ -0,0 +1,53 @@
|
||||
function initDarkModeSwitcher() {
|
||||
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
|
||||
if (themeToggleDarkIcon === null && themeToggleLightIcon === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('color-theme')) {
|
||||
if (localStorage.getItem('color-theme') === 'light') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
themeToggleLightIcon.classList.remove('hidden');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
themeToggleDarkIcon.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default to light mode if not set.
|
||||
document.documentElement.classList.remove('dark');
|
||||
themeToggleLightIcon.classList.remove('hidden');
|
||||
}
|
||||
|
||||
const themeToggleBtn = document.getElementById('theme-toggle');
|
||||
|
||||
let event = new Event('dark-mode');
|
||||
|
||||
themeToggleBtn.addEventListener('click', function () {
|
||||
// toggle icons
|
||||
themeToggleDarkIcon.classList.toggle('hidden');
|
||||
themeToggleLightIcon.classList.toggle('hidden');
|
||||
|
||||
// if set via local storage previously
|
||||
if (localStorage.getItem('color-theme')) {
|
||||
if (localStorage.getItem('color-theme') === 'light') {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.setItem('color-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.setItem('color-theme', 'light');
|
||||
}
|
||||
// if NOT set via local storage previously
|
||||
} else if (document.documentElement.classList.contains('dark')) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.setItem('color-theme', 'light');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.setItem('color-theme', 'dark');
|
||||
}
|
||||
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
11
src/AliasVault.Admin2/wwwroot/js/utilities.js
Normal file
11
src/AliasVault.Admin2/wwwroot/js/utilities.js
Normal file
@@ -0,0 +1,11 @@
|
||||
function downloadFileFromStream(fileName, contentStreamReference) {
|
||||
const arrayBuffer = new Uint8Array(contentStreamReference).buffer;
|
||||
const blob = new Blob([arrayBuffer]);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchorElement = document.createElement('a');
|
||||
anchorElement.href = url;
|
||||
anchorElement.download = fileName ?? '';
|
||||
anchorElement.click();
|
||||
anchorElement.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
Reference in New Issue
Block a user