diff --git a/src/SportsDivision.Application/DTOs/UserDto.cs b/src/SportsDivision.Application/DTOs/UserDto.cs index 3571678..bbeb3fc 100644 --- a/src/SportsDivision.Application/DTOs/UserDto.cs +++ b/src/SportsDivision.Application/DTOs/UserDto.cs @@ -1,4 +1,23 @@ +using System.ComponentModel.DataAnnotations; + namespace SportsDivision.Application.DTOs; public class UserDto { public string Id { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string FullName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } public string? SchoolName { get; set; } public bool IsActive { get; set; } } public class UserCreateDto { public string Email { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } } public class UserUpdateDto { public string Id { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; public string Role { get; set; } = string.Empty; public int? SchoolId { get; set; } public bool IsActive { get; set; } } +public class ResetPasswordDto +{ + public string Id { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string FullName { get; set; } = string.Empty; + + [Required(ErrorMessage = "New password is required.")] + [DataType(DataType.Password)] + [Display(Name = "New Password")] + public string NewPassword { get; set; } = string.Empty; + + [Required(ErrorMessage = "Please confirm the new password.")] + [DataType(DataType.Password)] + [Display(Name = "Confirm Password")] + [Compare(nameof(NewPassword), ErrorMessage = "Passwords do not match.")] + public string ConfirmPassword { get; set; } = string.Empty; +} diff --git a/src/SportsDivision.Web/Controllers/UserManagementController.cs b/src/SportsDivision.Web/Controllers/UserManagementController.cs index 580543a..61fcff6 100644 --- a/src/SportsDivision.Web/Controllers/UserManagementController.cs +++ b/src/SportsDivision.Web/Controllers/UserManagementController.cs @@ -202,6 +202,51 @@ public class UserManagementController : Controller return RedirectToAction(nameof(Index)); } + [HttpGet] + public async Task ResetPassword(string id) + { + var user = await _userManager.FindByIdAsync(id); + if (user == null) return NotFound(); + + return View(new ResetPasswordDto + { + Id = user.Id, + Email = user.Email ?? string.Empty, + FullName = user.FullName + }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task ResetPassword(ResetPasswordDto dto) + { + var user = await _userManager.FindByIdAsync(dto.Id); + if (user == null) return NotFound(); + + // Keep display fields populated regardless of the posted values. + dto.Email = user.Email ?? string.Empty; + dto.FullName = user.FullName; + + if (!ModelState.IsValid) + { + return View(dto); + } + + var token = await _userManager.GeneratePasswordResetTokenAsync(user); + var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword); + if (!result.Succeeded) + { + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return View(dto); + } + + TempData["SuccessMessage"] = $"Password reset successfully for {user.Email}."; + return RedirectToAction(nameof(Index)); + } + private async Task PopulateDropdowns() { ViewBag.Roles = await _roleManager.Roles.Select(r => r.Name).ToListAsync(); diff --git a/src/SportsDivision.Web/Views/UserManagement/Index.cshtml b/src/SportsDivision.Web/Views/UserManagement/Index.cshtml index f82957a..2374523 100644 --- a/src/SportsDivision.Web/Views/UserManagement/Index.cshtml +++ b/src/SportsDivision.Web/Views/UserManagement/Index.cshtml @@ -53,7 +53,7 @@ Role School Status - Actions + Actions @@ -78,6 +78,9 @@ + + +
@Html.AntiForgeryToken() @if (user.IsActive) diff --git a/src/SportsDivision.Web/Views/UserManagement/ResetPassword.cshtml b/src/SportsDivision.Web/Views/UserManagement/ResetPassword.cshtml new file mode 100644 index 0000000..5a87031 --- /dev/null +++ b/src/SportsDivision.Web/Views/UserManagement/ResetPassword.cshtml @@ -0,0 +1,52 @@ +@model ResetPasswordDto +@{ + ViewData["Title"] = "Reset Password"; +} + + + +
+
+
+
+

+ Set a new password for @Model.FullName (@Model.Email). + The user will need to sign in with this new password. +

+ + + @Html.AntiForgeryToken() + + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + Cancel +
+ +
+
+
+
+ +@section Scripts { + +}