Skip to content

Commit 0c8d10a

Browse files
committed
Refactor user management components
Refactored `PickSuperiorIdAutocomplete` to `PickSuperiorAutocomplete` with improved filtering logic and modularity. Removed `UserForm.razor` and integrated its functionality into `UserFormDialog.razor`, consolidating user management features like role assignment and profile picture upload. Updated `Theme.cs` with improved color schemes for better accessibility. Incremented app version to `1.3.17` in `appsettings.json`. Applied namespace and formatting adjustments for consistency.
1 parent 28e719e commit 0c8d10a

5 files changed

Lines changed: 159 additions & 191 deletions

File tree

src/Server.UI/Components/Autocompletes/PickSuperiorIdAutocomplete.razor.cs renamed to src/Server.UI/Components/Autocompletes/PickSuperiorAutocomplete.razor.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity;
1+
using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity;
22
using CleanArchitecture.Blazor.Application.Features.Identity.DTOs;
33
using CleanArchitecture.Blazor.Infrastructure.Services.Identity;
44

55
namespace CleanArchitecture.Blazor.Server.UI.Components.Autocompletes;
66
#nullable disable warnings
7-
public class PickSuperiorIdAutocomplete<T> : MudAutocomplete<ApplicationUserDto>
7+
public class PickSuperiorAutocomplete<T> : MudAutocomplete<ApplicationUserDto>
88
{
9-
public PickSuperiorIdAutocomplete()
9+
public PickSuperiorAutocomplete()
1010
{
1111
SearchFunc = SearchKeyValues;
1212
ToStringFunc = dto => dto?.UserName;
@@ -24,9 +24,9 @@ public PickSuperiorIdAutocomplete()
2424
private Task<IEnumerable<ApplicationUserDto>> SearchKeyValues(string? value, CancellationToken cancellation)
2525
{
2626
var result = UserService.DataSource.Where(x =>
27-
x.TenantId != null && x.TenantId.Equals(TenantId) && !x.UserName.Equals(OwnerName));
27+
(x.TenantId != null && x.TenantId.Equals(TenantId) || TenantId==null) && !x.UserName.Equals(OwnerName));
2828
if (!string.IsNullOrWhiteSpace(value))
29-
result = UserService.DataSource.Where(x => x.TenantId.Equals(TenantId) && !x.UserName.Equals(OwnerName) &&
29+
result = UserService.DataSource.Where(x => (x.TenantId != null && x.TenantId.Equals(TenantId) || TenantId == null) && !x.UserName.Equals(OwnerName) &&
3030
(x.UserName.Contains(value,
3131
StringComparison.OrdinalIgnoreCase) ||
3232
x.Email.Contains(value, StringComparison.OrdinalIgnoreCase)));

src/Server.UI/Pages/Identity/Users/Components/UserForm.razor

Lines changed: 0 additions & 170 deletions
This file was deleted.
Lines changed: 150 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,111 @@
1-
@using CleanArchitecture.Blazor.Application.Features.Identity.DTOs
1+
@using CleanArchitecture.Blazor.Application.Features.Identity.DTOs
2+
@using CleanArchitecture.Blazor.Application.Features.Tenants.DTOs
3+
@using CleanArchitecture.Blazor.Domain.Identity
4+
@using CleanArchitecture.Blazor.Domain.Common.Enums
25
@inherits MudComponentBase
36

47
@inject IStringLocalizer<Identity.Users.Users> L
8+
@inject IValidationService Validator
9+
@inject IUploadService UploadService
10+
@inject RoleManager<ApplicationRole> RoleManager
11+
@inject ISnackbar Snackbar
512

613
<MudDialog>
714
<DialogContent>
8-
<UserForm UserProfile="@UserProfile" @ref="@_userForm" Model="Model" OnFormSubmit="OnFormSubmitHandler"></UserForm>
15+
<MudForm Model="@Model" @ref="_form" Validation="@Validator.ValidateValue(Model)">
16+
<input For="@(() => Model.Id)" @bind-value="Model.Id" type="hidden" />
17+
<input For="@(() => Model.ProfilePictureDataUrl)" @bind-value="Model.ProfilePictureDataUrl" type="hidden" />
18+
19+
<MudGrid Spacing="2">
20+
<MudItem sm="12">
21+
<div class="d-flex justify-center">
22+
<MudAvatar Style="height:128px; width:128px; font-size:2rem;">
23+
@if (string.IsNullOrEmpty(Model.ProfilePictureDataUrl))
24+
{
25+
@Model.UserName.ToUpper().FirstOrDefault()
26+
}
27+
else
28+
{
29+
<MudImage Src="@Model.ProfilePictureDataUrl" />
30+
}
31+
</MudAvatar>
32+
<MudTooltip Text="@L["Click upload a image"]">
33+
<MudFileUpload T="IBrowserFile" Accept=".jpg, .jpeg, .png, .webp" FilesChanged="UploadPhoto" Style="margin-top:-10px;margin-left:-15px">
34+
<ActivatorContent>
35+
<MudIconButton Icon="@Icons.Material.Filled.PhotoCamera" />
36+
</ActivatorContent>
37+
</MudFileUpload>
38+
</MudTooltip>
39+
</div>
40+
</MudItem>
41+
42+
<MudItem sm="6" xs="12">
43+
<MultiTenantAutocomplete For="@(() => Model.Tenant)"
44+
T="TenantDto"
45+
Label="@L[Model.GetMemberDescription(x => x.Tenant)]"
46+
Required="true"
47+
Value="@Model.Tenant"
48+
ValueChanged="TenantChanged" />
49+
</MudItem>
50+
<MudItem xs="12" sm="6">
51+
<MudTextField For="@(() => Model.Provider)" @bind-Value="Model.Provider" Label="@L[Model.GetMemberDescription(x => x.Provider)]" ReadOnly="true" />
52+
</MudItem>
53+
<MudItem xs="12" sm="6">
54+
<MudTextField For="@(() => Model.UserName)" @bind-Value="Model.UserName" Label="@L[Model.GetMemberDescription(x => x.UserName)]" Required="true" />
55+
</MudItem>
56+
<MudItem xs="12" sm="6">
57+
<MudCheckBox Color="Color.Primary" For="@(() => Model.IsActive)" @bind-Value="Model.IsActive" Label="@L[Model.GetMemberDescription(x => x.IsActive)]" />
58+
</MudItem>
59+
<MudItem xs="12" sm="6">
60+
<MudTextField For="@(() => Model.DisplayName)" @bind-Value="Model.DisplayName" Label="@L[Model.GetMemberDescription(x => x.DisplayName)]" />
61+
</MudItem>
62+
<MudItem xs="12" sm="6">
63+
<PickSuperiorAutocomplete For="@(() => Model.Superior)" T="ApplicationUserDto"
64+
OwnerName="@Model.UserName"
65+
Clearable="true"
66+
Label="@L[Model.GetMemberDescription(x => x.Superior)]"
67+
@bind-Value="Model.Superior" />
68+
</MudItem>
69+
<MudItem xs="12" sm="6">
70+
<MudTextField For="@(() => Model.Email)" @bind-Value="Model.Email" Label="@L[Model.GetMemberDescription(x => x.Email)]" Required="true" />
71+
</MudItem>
72+
<MudItem xs="12" sm="6">
73+
<MudTextField For="@(() => Model.PhoneNumber)" @bind-Value="Model.PhoneNumber" Label="@L[Model.GetMemberDescription(x => x.PhoneNumber)]" />
74+
</MudItem>
75+
<MudItem sm="6" xs="12">
76+
<TimeZoneAutocomplete T="string" For="@(() => Model.TimeZoneId)" @bind-Value="Model.TimeZoneId" Label="@L[Model.GetMemberDescription(x => x.TimeZoneId)]" />
77+
</MudItem>
78+
<MudItem sm="6" xs="12">
79+
<LanguageAutocomplete T="string" For="@(() => Model.LanguageCode)" @bind-Value="Model.LanguageCode" Label="@L[Model.GetMemberDescription(x => x.LanguageCode)]" />
80+
</MudItem>
81+
82+
<MudItem xs="12" sm="12">
83+
<MudText Typo="Typo.body1">@L["Assign Roles"]</MudText>
84+
<MudGrid Class="mt-1" Spacing="2">
85+
@if (Roles.Any())
86+
{
87+
@foreach (var role in Roles)
88+
{
89+
<MudItem xs="6" sm="6" Class="py-0 my=0">
90+
<MudCheckBox Color="Color.Primary" T="bool" Value="@role.Value" Label="@role.Key" ValueChanged="@(s => role.Value = s)" />
91+
</MudItem>
92+
}
93+
}
94+
else
95+
{
96+
<MudAlert Class="flex-grow-1" Severity="Severity.Warning">@L["No roles available for this tenant"]</MudAlert>
97+
}
98+
</MudGrid>
99+
</MudItem>
100+
101+
<MudItem sm="6" xs="12">
102+
<ReadOnlyField Value="@(Model.CreatedAt.ToLocalTime(UserProfile?.LocalTimeOffset))" Label="@L[Model.GetMemberDescription(x => x.CreatedAt)]" />
103+
</MudItem>
104+
<MudItem sm="6" xs="12">
105+
<ReadOnlyField Value="@(Model.LastModifiedAt.ToLocalTime(UserProfile?.LocalTimeOffset))" Label="@L[Model.GetMemberDescription(x => x.LastModifiedAt)]" />
106+
</MudItem>
107+
</MudGrid>
108+
</MudForm>
9109
</DialogContent>
10110
<DialogActions>
11111
<MudButton OnClick="Cancel">@AppStrings.Cancel</MudButton>
@@ -15,26 +115,64 @@
15115

16116
@code{
17117
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!;
18-
[Parameter] public UserProfile? UserProfile { get; set; }
118+
[CascadingParameter] public UserProfile? UserProfile { get; set; }
19119
[Parameter] public ApplicationUserDto Model { get; set; } = default!;
20-
21120

22-
private UserForm? _userForm;
121+
private MudForm? _form;
122+
private List<CheckItem> Roles { get; set; } = new();
23123

24-
protected async Task Submit()
124+
private class CheckItem
25125
{
26-
await _userForm!.Submit();
126+
public string Key { get; set; } = string.Empty;
127+
public bool Value { get; set; }
27128
}
28129

29-
private void Cancel()
130+
protected override async Task OnInitializedAsync()
30131
{
31-
MudDialog.Cancel();
132+
await GetRoles(Model.TenantId);
32133
}
33134

34-
protected Task OnFormSubmitHandler(ApplicationUserDto model)
135+
private async Task GetRoles(string? tenantId)
35136
{
36-
MudDialog.Close(DialogResult.Ok(model));
37-
return Task.CompletedTask;
137+
var roles = await RoleManager.Roles
138+
.Where(x => x.TenantId == tenantId)
139+
.Select(x => x.Name)
140+
.ToListAsync();
141+
Roles = roles.Select(r => new CheckItem { Key = r!, Value = Model.AssignedRoles?.Contains(r) ?? false }).ToList();
38142
}
39143

144+
private async Task TenantChanged(TenantDto? tenant)
145+
{
146+
Model.TenantId = tenant?.Id;
147+
Model.Tenant = tenant;
148+
await GetRoles(Model.TenantId);
149+
}
150+
151+
private async Task UploadPhoto(IBrowserFile file)
152+
{
153+
using var ms = new MemoryStream();
154+
await file.OpenReadStream(GlobalVariables.MaxAllowedSize).CopyToAsync(ms);
155+
var resizedStream = await ImageProcessor.ResizeAndCompressToJpegAsync(ms,128,128,80);
156+
var uploadRequest = new UploadRequest(file.Name, UploadType.ProfilePicture, resizedStream.ToArray(), overwrite: true, Model.Id);
157+
Model.ProfilePictureDataUrl = await UploadService.UploadAsync(uploadRequest);
158+
Snackbar.Add(AppStrings.UploadSuccess, Severity.Info);
159+
}
160+
161+
protected async Task Submit()
162+
{
163+
if (_form is not null)
164+
{
165+
await _form.Validate();
166+
if (_form.IsValid)
167+
{
168+
Model.AssignedRoles = Roles.Where(r => r.Value).Select(r => r.Key).ToArray();
169+
MudDialog.Close(DialogResult.Ok(Model));
170+
}
171+
}
172+
}
173+
174+
private void Cancel()
175+
{
176+
MudDialog.Cancel();
177+
}
40178
}

0 commit comments

Comments
 (0)