Creata pagina login con Syncfusion
This commit is contained in:
41
SmartDB/Components/Admin/Dtos/CreateUserDto.cs
Normal file
41
SmartDB/Components/Admin/Dtos/CreateUserDto.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SmartDB.Components.Admin.Dtos
|
||||
{
|
||||
/// <summary>
|
||||
/// DTO per la creazione di un nuovo utente da parte dell'amministratore
|
||||
/// </summary>
|
||||
public class CreateUserDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Email dell'utente
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "L'email <20> obbligatoria")]
|
||||
[EmailAddress(ErrorMessage = "Inserisci un'email valida")]
|
||||
public string Email { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Nome dell'utente
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Il nome <20> obbligatorio")]
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Cognome dell'utente
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Il cognome <20> obbligatorio")]
|
||||
public string LastName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Password temporanea per l'utente
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "La password <20> obbligatoria")]
|
||||
[StringLength(100, MinimumLength = 6, ErrorMessage = "La password deve avere almeno 6 caratteri")]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Ruolo da assegnare all'utente
|
||||
/// </summary>
|
||||
public string Role { get; set; } = "User";
|
||||
}
|
||||
}
|
||||
265
SmartDB/Components/Admin/Pages/Users.razor
Normal file
265
SmartDB/Components/Admin/Pages/Users.razor
Normal file
@@ -0,0 +1,265 @@
|
||||
@page "/admin/users"
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using SmartDB.Components.Admin.Dtos
|
||||
@using SmartDB.Components.Admin.Services
|
||||
@using SmartDB.Data
|
||||
@attribute [Authorize(Policy = "AdminOnly")]
|
||||
|
||||
@inject IUserManagementService UserService
|
||||
@inject ILogger<Users> Logger
|
||||
|
||||
<PageTitle>Gestione Utenti - Admin</PageTitle>
|
||||
|
||||
<h1>Gestione Utenti</h1>
|
||||
|
||||
@if (!string.IsNullOrEmpty(successMessage))
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
@successMessage
|
||||
<button type="button" class="btn-close" @onclick="@(() => successMessage = string.Empty)" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
@errorMessage
|
||||
<button type="button" class="btn-close" @onclick="@(() => errorMessage = string.Empty)" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<ul class="nav nav-tabs mb-3" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link @(activeTab == "list" ? "active" : "")" @onclick="@(() => activeTab = "list")" type="button" role="tab" aria-selected="@(activeTab == "list")">
|
||||
Elenco Utenti
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link @(activeTab == "create" ? "active" : "")" @onclick="@(() => activeTab = "create")" type="button" role="tab" aria-selected="@(activeTab == "create")">
|
||||
Aggiungi Utente
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
@if (activeTab == "list")
|
||||
{
|
||||
<div class="tab-pane fade show active">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>Nome</th>
|
||||
<th>Cognome</th>
|
||||
<th>Ruolo</th>
|
||||
<th>Stato</th>
|
||||
<th>Data Creazione</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (users != null && users.Any())
|
||||
{
|
||||
@foreach (var user in users)
|
||||
{
|
||||
<tr>
|
||||
<td>@user.Email</td>
|
||||
<td>@user.FirstName</td>
|
||||
<td>@user.LastName</td>
|
||||
<td>
|
||||
<span class="badge bg-info">@GetUserRole(user.Id)</span>
|
||||
</td>
|
||||
<td>
|
||||
@if (user.IsActive)
|
||||
{
|
||||
<span class="badge bg-success">Attivo</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-danger">Disattivo</span>
|
||||
}
|
||||
</td>
|
||||
<td>@user.CreatedAt.ToShortDateString()</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-warning" @onclick="@(() => ToggleUserStatus(user.Id))" title="Cambia stato">
|
||||
@(user.IsActive ? "Disabilita" : "Abilita")
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" @onclick="@(() => DeleteUser(user.Id))" title="Elimina">
|
||||
Elimina
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr>
|
||||
<td colspan="7" class="text-center">Nessun utente trovato</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (activeTab == "create")
|
||||
{
|
||||
<div class="tab-pane fade show active">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Aggiungi Nuovo Utente</h3>
|
||||
<EditForm Model="newUser" OnValidSubmit="HandleCreateUser">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary class="text-danger" />
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<InputText id="email" class="form-control" @bind-Value="newUser.Email" placeholder="utente@example.com" />
|
||||
<ValidationMessage For="@(() => newUser.Email)" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="firstName" class="form-label">Nome</label>
|
||||
<InputText id="firstName" class="form-control" @bind-Value="newUser.FirstName" placeholder="Mario" />
|
||||
<ValidationMessage For="@(() => newUser.FirstName)" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="lastName" class="form-label">Cognome</label>
|
||||
<InputText id="lastName" class="form-control" @bind-Value="newUser.LastName" placeholder="Rossi" />
|
||||
<ValidationMessage For="@(() => newUser.LastName)" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password Temporanea</label>
|
||||
<InputText type="password" id="password" class="form-control" @bind-Value="newUser.Password" placeholder="Min 6 caratteri" />
|
||||
<ValidationMessage For="@(() => newUser.Password)" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Ruolo</label>
|
||||
<InputSelect id="role" class="form-control" @bind-Value="newUser.Role">
|
||||
<option value="User">Utente</option>
|
||||
<option value="Admin">Amministratore</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" disabled="@isSubmitting">
|
||||
@if (isSubmitting)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span>Creazione in corso...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Crea Utente</span>
|
||||
}
|
||||
</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string activeTab = "list";
|
||||
private List<ApplicationUser>? users;
|
||||
private CreateUserDto newUser = new();
|
||||
private string successMessage = string.Empty;
|
||||
private string errorMessage = string.Empty;
|
||||
private bool isSubmitting = false;
|
||||
private Dictionary<string, string> userRoles = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadUsers();
|
||||
}
|
||||
|
||||
private async Task LoadUsers()
|
||||
{
|
||||
try
|
||||
{
|
||||
users = await UserService.GetAllUsersAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Errore nel caricamento degli utenti");
|
||||
errorMessage = "Errore nel caricamento degli utenti";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleCreateUser()
|
||||
{
|
||||
isSubmitting = true;
|
||||
try
|
||||
{
|
||||
var (success, message) = await UserService.CreateUserAsync(newUser);
|
||||
if (success)
|
||||
{
|
||||
successMessage = message;
|
||||
newUser = new();
|
||||
await LoadUsers();
|
||||
activeTab = "list";
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = message;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Errore nella creazione dell'utente");
|
||||
errorMessage = "Errore nella creazione dell'utente";
|
||||
}
|
||||
finally
|
||||
{
|
||||
isSubmitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteUser(string userId)
|
||||
{
|
||||
if (await JsConfirm("Sei sicuro di voler eliminare questo utente?"))
|
||||
{
|
||||
var (success, message) = await UserService.DeleteUserAsync(userId);
|
||||
if (success)
|
||||
{
|
||||
successMessage = message;
|
||||
await LoadUsers();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleUserStatus(string userId)
|
||||
{
|
||||
var (success, message) = await UserService.ToggleUserStatusAsync(userId);
|
||||
if (success)
|
||||
{
|
||||
successMessage = message;
|
||||
await LoadUsers();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetUserRole(string userId)
|
||||
{
|
||||
// Per ora returniamo "User" - in futuro implementeremo la logica per recuperare i ruoli
|
||||
return "User";
|
||||
}
|
||||
|
||||
private async Task<bool> JsConfirm(string message)
|
||||
{
|
||||
// Placeholder - in un componente reale userebbero JS interop
|
||||
return true;
|
||||
}
|
||||
}
|
||||
155
SmartDB/Components/Admin/Services/UserManagementService.cs
Normal file
155
SmartDB/Components/Admin/Services/UserManagementService.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SmartDB.Components.Admin.Dtos;
|
||||
using SmartDB.Data;
|
||||
|
||||
namespace SmartDB.Components.Admin.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Servizio per la gestione degli utenti dell'applicazione
|
||||
/// </summary>
|
||||
public interface IUserManagementService
|
||||
{
|
||||
/// <summary>
|
||||
/// Crea un nuovo utente con il ruolo specificato
|
||||
/// </summary>
|
||||
Task<(bool Success, string Message)> CreateUserAsync(CreateUserDto dto);
|
||||
|
||||
/// <summary>
|
||||
/// Recupera tutti gli utenti
|
||||
/// </summary>
|
||||
Task<List<ApplicationUser>> GetAllUsersAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Elimina un utente
|
||||
/// </summary>
|
||||
Task<(bool Success, string Message)> DeleteUserAsync(string userId);
|
||||
|
||||
/// <summary>
|
||||
/// Disabilita/abilita un utente
|
||||
/// </summary>
|
||||
Task<(bool Success, string Message)> ToggleUserStatusAsync(string userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementazione del servizio di gestione degli utenti
|
||||
/// </summary>
|
||||
public class UserManagementService : IUserManagementService
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
|
||||
public UserManagementService(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_roleManager = roleManager;
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> CreateUserAsync(CreateUserDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Verifica se l'email <20> gi<67> in uso
|
||||
var existingUser = await _userManager.FindByEmailAsync(dto.Email);
|
||||
if (existingUser != null)
|
||||
{
|
||||
return (false, "Un utente con questa email esiste gi<67>");
|
||||
}
|
||||
|
||||
// Crea il nuovo utente
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = dto.Email,
|
||||
Email = dto.Email,
|
||||
FirstName = dto.FirstName,
|
||||
LastName = dto.LastName,
|
||||
EmailConfirmed = true,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
var result = await _userManager.CreateAsync(user, dto.Password);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
return (false, $"Errore nella creazione dell'utente: {errors}");
|
||||
}
|
||||
|
||||
// Assegna il ruolo
|
||||
var roleExist = await _roleManager.RoleExistsAsync(dto.Role);
|
||||
if (!roleExist)
|
||||
{
|
||||
dto.Role = "User"; // Fallback al ruolo User
|
||||
}
|
||||
|
||||
var roleResult = await _userManager.AddToRoleAsync(user, dto.Role);
|
||||
if (!roleResult.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", roleResult.Errors.Select(e => e.Description));
|
||||
return (false, $"Errore nell'assegnazione del ruolo: {errors}");
|
||||
}
|
||||
|
||||
return (true, "Utente creato con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<ApplicationUser>> GetAllUsersAsync()
|
||||
{
|
||||
return _userManager.Users.ToList();
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> DeleteUserAsync(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, "Utente non trovato");
|
||||
}
|
||||
|
||||
var result = await _userManager.DeleteAsync(user);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
return (false, $"Errore nell'eliminazione: {errors}");
|
||||
}
|
||||
|
||||
return (true, "Utente eliminato con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Message)> ToggleUserStatusAsync(string userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return (false, "Utente non trovato");
|
||||
}
|
||||
|
||||
user.IsActive = !user.IsActive;
|
||||
var result = await _userManager.UpdateAsync(user);
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
var errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
return (false, $"Errore nell'aggiornamento: {errors}");
|
||||
}
|
||||
|
||||
return (true, $"Utente {(user.IsActive ? "abilitato" : "disabilitato")} con successo");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Errore: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
SmartDB/Components/Admin/_Imports.razor
Normal file
11
SmartDB/Components/Admin/_Imports.razor
Normal file
@@ -0,0 +1,11 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using SmartDB
|
||||
@using SmartDB.Components
|
||||
@using SmartDB.Data
|
||||
Reference in New Issue
Block a user