Skip to content

Commit

Permalink
Merge pull request neozhu#724 from neozhu/improvement
Browse files Browse the repository at this point in the history
improvement
  • Loading branch information
neozhu authored Aug 15, 2024
2 parents 6df4f82 + f7464b1 commit 501edcd
Show file tree
Hide file tree
Showing 37 changed files with 1,172 additions and 993 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Experience the application in action in Blazor Server mode by visiting
## Visual Insights

Dive into the application's aesthetics and functionality through screenshots and a video walkthrough.
[![Everything Is AWESOME](doc/page.png)](https://www.youtube.com/embed/GyZJl_dG-Pg "Everything Is AWESOME")
[![Everything Is AWESOME](doc/logo.png)](https://www.youtube.com/embed/GyZJl_dG-Pg "Everything Is AWESOME")

## Development Setup

Expand Down
Binary file added doc/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Ardalis.Specification" Version="8.0.0" />
<PackageReference Include="Ardalis.Specification.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="ClosedXML" Version="0.104.0-preview2" />
Expand All @@ -20,8 +20,8 @@
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
<PackageReference Include="LazyCache" Version="2.4.0" />
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.8" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.4" />
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Domain/Domain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<ItemGroup>
<PackageReference Include="MediatR" Version="12.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static class DependencyInjection
private const string DEFAULT_FROM_EMAIL = "[email protected]";
private const string LOGIN_PATH = "/pages/authentication/login";
private const int DEFAULT_LOCKOUT_TIME_SPAN_MINUTES = 5;
private const int MAX_FAILED_ACCESS_ATTEMPTS = 10;
private const int MAX_FAILED_ACCESS_ATTEMPTS = 5;

public static IServiceCollection AddInfrastructure(this IServiceCollection services,
IConfiguration configuration)
Expand Down
24 changes: 12 additions & 12 deletions src/Infrastructure/Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@
<ItemGroup>

<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="8.0.8" />

<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.7">
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.7" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="QuestPDF" Version="2024.7.2" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="6.6.1" />
Expand Down
1 change: 1 addition & 0 deletions src/Infrastructure/PermissionSet/Permissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public static class Users
public const string RestPassword = "Permissions.Users.RestPassword";
public const string SendRestPasswordMail = "Permissions.Users.SendRestPasswordMail";
public const string ManagePermissions = "Permissions.Users.Permissions";
public const string Deactivation = "Permissions.Users.Activation/Deactivation";
}

[DisplayName("Roles")]
Expand Down
2 changes: 1 addition & 1 deletion src/Server.UI/Components/Fusion/OnlineUsersTracker.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<div class="d-flex flex-row gap-2 my-3 gap-2 my-3">
@foreach (var user in State.LastNonErrorValue.OrderBy(u => u.Name != currentUserName))
{
<MudBadge Color="Color.Success" Overlap="true" Bordered="true">
<MudBadge Color="Color.Success" Overlap="false" Dot="true" Bordered="true">
@if (string.IsNullOrEmpty(user.ProfilePictureDataUrl))
{
<MudAvatar title="@user.Name">
Expand Down
12 changes: 10 additions & 2 deletions src/Server.UI/Components/Routes.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<Authorizing>
<text>@L["Please wait, we are authorizing you..."]</text>
<MudPaper Class="pa-6 text-center">
<MudProgressCircular Indeterminate="true" Color="Color.Primary" Size="Size.Large" Class="mb-4" />
<MudText Typo="Typo.h6">@L["Please wait, we are authorizing you..."]</MudText>
</MudPaper>
</Authorizing>
<NotAuthorized>
<text>@L["not authorized"]</text>
<MudPaper Class="pa-6 text-center">
<MudIcon Icon="@Icons.Material.Filled.Warning" Color="Color.Error" Size="Size.Large" Class="mb-4" />
<MudText Typo="Typo.h5">@L["You are not authorized to view this page."]</MudText>
<MudText Typo="Typo.body1" Class="mt-2">@L["If you need access to this page, please contact the administrator."]</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" Class="mt-4">@L["Contact Administrator"]</MudButton>
</MudPaper>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
Expand Down
27 changes: 21 additions & 6 deletions src/Server.UI/Components/Shared/Layout/PublicLayout.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
@layout MainLayout
@using Microsoft.AspNetCore.Antiforgery
@layout MainLayout
@inherits LayoutComponentBase
@inject LayoutService LayoutService
@inject IAntiforgery _antiforgery;
@inject IHttpContextAccessor _httpContextAccessor;
@inject IStringLocalizer<SharedResource> L
<MudLayout>
<MudAppBar Elevation="0"
Expand All @@ -15,9 +18,6 @@
</MudText>
</NavLink>
<MudSpacer/>
<form method="post" hidden>
<AntiforgeryToken />
</form>
<MudHidden Breakpoint="Breakpoint.SmAndDown">
<MudTooltip Arrow="true"
Text="Navigate to GitHub">
Expand All @@ -37,7 +37,7 @@
<AuthorizeView>
<Authorized>
<form action="@IdentityComponentsEndpointRouteBuilderExtensions.Logout" method="post">
<AntiforgeryToken/>
<input type="hidden" name="__RequestVerificationToken" value="@antiforgeryToken" />
<input type="hidden" name="ReturnUrl" value="/"/>
<MudTooltip Text="@L["Logout"]">
<MudButton Style="text-transform:none"
Expand Down Expand Up @@ -66,7 +66,7 @@
<AuthorizeView>
<Authorized>
<form action="@IdentityComponentsEndpointRouteBuilderExtensions.Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="__RequestVerificationToken" value="@antiforgeryToken" />
<input type="hidden" name="ReturnUrl" value="/" />
<MudTooltip Text="@L["Logout"]">
<MudButton Style="text-transform:none"
Expand Down Expand Up @@ -101,4 +101,19 @@

@code
{
private string? antiforgeryToken;
protected override void OnInitialized()
{
antiforgeryToken = getAntiforgeryToken();
}
private string? getAntiforgeryToken()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null)
{
throw new InvalidOperationException("HttpContext is not available.");
}
var tokens = _antiforgery.GetAndStoreTokens(httpContext);
return tokens.RequestToken;
}
}
24 changes: 18 additions & 6 deletions src/Server.UI/Components/Shared/Layout/UserMenu.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
@inject IStringLocalizer<HeaderMenu> L
@using Microsoft.AspNetCore.Antiforgery
@inject IStringLocalizer<HeaderMenu> L
@inject UserProfileStateService UserProfileStateService
@inject IAntiforgery _antiforgery;
@inject IHttpContextAccessor _httpContextAccessor;
@implements IDisposable
<MudTooltip Arrow="true" Text="@L["User Profile"]">
<MudMenu AnchorOrigin="Origin.BottomRight"
Expand Down Expand Up @@ -56,7 +59,7 @@
</MudMenuItem>
<div class="mt-4 mx-4">
<form action="@IdentityComponentsEndpointRouteBuilderExtensions.Logout" method="post">
<AntiforgeryToken/>
<input type="hidden" name="__RequestVerificationToken" value="@antiforgeryToken" />
<input type="hidden" name="ReturnUrl" value="/pages/authentication/login" />
<MudButton Color="Color.Secondary"
ButtonType="ButtonType.Submit"
Expand All @@ -70,20 +73,29 @@
</div>
</ChildContent>
</MudMenu>
<form method="post" hidden>
<AntiforgeryToken/>
</form>

</MudTooltip>

@code
{
[Parameter] public EventCallback<MouseEventArgs> OnSettingClick { get; set; }
private UserProfile UserProfile => UserProfileStateService.UserProfile;
private string? antiforgeryToken = string.Empty;
protected override void OnInitialized()
{
UserProfileStateService.OnChange += StateHasChanged;
antiforgeryToken = getAntiforgeryToken();
}
private string? getAntiforgeryToken()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null)
{
throw new InvalidOperationException("HttpContext is not available.");
}
var tokens = _antiforgery.GetAndStoreTokens(httpContext);
return tokens.RequestToken;
}

public void Dispose()
{
UserProfileStateService.OnChange -= StateHasChanged;
Expand Down
2 changes: 1 addition & 1 deletion src/Server.UI/Pages/Identity/Authentication/Login.razor
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@

// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await SignInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, false);
var result = await SignInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, true);
if (result.Succeeded)
{
Logger.LogInformation("{UserName} has logged in.",Input.UserName);
Expand Down
24 changes: 14 additions & 10 deletions src/Server.UI/Pages/Identity/Users/Users.razor
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
</PropertyColumn>
<PropertyColumn Property="x => x.UserName" Title="@L[_currentDto.GetMemberDescription(x => x.UserName)]">
<CellTemplate>
<div class="d-flex align-items-center">
<div class="d-flex align-center">
<MudBadge Color="@(IsOnline(context.Item.DisplayName??context.Item.UserName) ? Color.Success : Color.Error)" Overlap="false" Dot="true" Bordered="true">
<MudAvatar>
@if (string.IsNullOrEmpty(context.Item.ProfilePictureDataUrl))
Expand Down Expand Up @@ -286,19 +286,18 @@
<PropertyColumn Property="x => x.IsActive" Title="@L[_currentDto.GetMemberDescription(x => x.IsActive)]">
<CellTemplate>
<div>
@if (!context.Item.IsActive || (context.Item.LockoutEnd is not null && context.Item.LockoutEnd > DateTime.UtcNow))
@if (!context.Item.IsActive)
{
<MudTooltip Text="@L["Click to change status to active."]" Delay="300">
<MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Close" ThumbIconColor="Color.Error">
<MudTooltip Text="@(_canDeactivation?L["Click to change status to active."]:L["No permission to change status."])" Delay="300">
<MudSwitch Disabled="@(!_canDeactivation)" T="bool" Value="@(context.Item.IsActive)" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Close" ThumbIconColor="Color.Error">
</MudSwitch>
</MudTooltip>
}
else
{
<MudTooltip Text="@L["Click to change status to inactive."]" Delay="300">
<MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Done" ThumbIconColor="Color.Success">
<MudTooltip Text="@(_canDeactivation?L["Click to change status to inactive."]:L["No permission to change status."])" Delay="300">
<MudSwitch Disabled="@(!_canDeactivation)" T="bool" Value="context.Item.IsActive" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Done" ThumbIconColor="Color.Success">
</MudSwitch>

</MudTooltip>
}
</div>
Expand Down Expand Up @@ -352,6 +351,7 @@
private bool _canManagePermissions;
private bool _canImport;
private bool _canExport;
private bool _canDeactivation;
private bool _loading;
private bool _exporting;
private bool _uploading;
Expand All @@ -373,6 +373,7 @@
_canManagePermissions = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.ManagePermissions)).Succeeded;
_canImport = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Import)).Succeeded;
_canExport = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Export)).Succeeded;
_canDeactivation = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Deactivation)).Succeeded;
_roles = await RoleManager.Roles.Select(x => x.Name).ToListAsync();


Expand Down Expand Up @@ -520,9 +521,14 @@
user.DisplayName = item.DisplayName;
user.Provider = item.Provider;
user.UserName = item.UserName;
user.IsActive = item.IsActive;

user.TenantId = item.TenantId;
user.SuperiorId = item.SuperiorId;
if (user.IsActive == false && item.IsActive && user.LockoutEnd is not null)
{
user.LockoutEnd = null;
}
user.IsActive = item.IsActive;
var identityResult = await UserManager.UpdateAsync(user);
if (identityResult.Succeeded)
{
Expand Down Expand Up @@ -696,13 +702,11 @@
private async Task DeactivateUser(ApplicationUser user, ApplicationUserDto item)
{
user.IsActive = false;
user.LockoutEnd = DateTimeOffset.MaxValue;
var identityResult = await UserManager.UpdateAsync(user);

if (identityResult.Succeeded)
{
item.IsActive = false;
item.LockoutEnd = DateTimeOffset.MaxValue;
Snackbar.Add($"{L["The user has been inactivated."]}", Severity.Info);
}
else
Expand Down
6 changes: 6 additions & 0 deletions src/Server.UI/Resources/Pages/Identity/Users/Users.ca-ES.resx
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,10 @@
<data name="Click to change status to inactive." xml:space="preserve">
<value>Feu clic per canviar l'estat a inactiu.</value>
</data>
<data name="No permission to change status." xml:space="preserve">
<value>No hi ha permís per canviar l'estat.</value>
</data>
<data name="Send Reset Password Email" xml:space="preserve">
<value>Envieu un correu electrònic de restabliment de la contrasenya</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/Server.UI/Resources/Pages/Identity/Users/Users.de-DE.resx
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,10 @@
<data name="Click to change status to inactive." xml:space="preserve">
<value>Klicken Sie, um den Status auf „inaktiv“ zu ändern.</value>
</data>
<data name="No permission to change status." xml:space="preserve">
<value>Keine Berechtigung zur Statusänderung.</value>
</data>
<data name="Send Reset Password Email" xml:space="preserve">
<value>E-Mail zum Zurücksetzen des Passworts senden</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/Server.UI/Resources/Pages/Identity/Users/Users.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,10 @@
<data name="Click to change status to inactive." xml:space="preserve">
<value>Click to change status to inactive.</value>
</data>
<data name="No permission to change status." xml:space="preserve">
<value>No permission to change status.</value>
</data>
<data name="Send Reset Password Email" xml:space="preserve">
<value>Send Reset Password Email</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/Server.UI/Resources/Pages/Identity/Users/Users.es-ES.resx
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,10 @@
<data name="Click to change status to inactive." xml:space="preserve">
<value>Haga clic para cambiar el estado a inactivo.</value>
</data>
<data name="No permission to change status." xml:space="preserve">
<value>No hay permiso para cambiar el estado.</value>
</data>
<data name="Send Reset Password Email" xml:space="preserve">
<value>Enviar correo electrónico para restablecer contraseña</value>
</data>
</root>
9 changes: 9 additions & 0 deletions src/Server.UI/Resources/Pages/Identity/Users/Users.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,13 @@
<data name="Click to change status to inactive." xml:space="preserve">
<value>Cliquez pour changer le statut en inactif.</value>
</data>
<data name="Full Name" xml:space="preserve">
<value>Nom et prénom</value>
</data>
<data name="No permission to change status." xml:space="preserve">
<value>Aucune autorisation de changer de statut.</value>
</data>
<data name="Send Reset Password Email" xml:space="preserve">
<value>Envoyer un e-mail de réinitialisation du mot de passe</value>
</data>
</root>
Loading

0 comments on commit 501edcd

Please sign in to comment.