Skip to content

Commit

Permalink
Merge pull request #28 from SP-SDU/RespectMathias/add-moderator-pages
Browse files Browse the repository at this point in the history
Add moderator pages with limited CRUD operations
  • Loading branch information
RespectMathias authored Dec 8, 2024
2 parents 9400d02 + 128569b commit 12a6d48
Show file tree
Hide file tree
Showing 23 changed files with 708 additions and 11 deletions.
31 changes: 28 additions & 3 deletions GameLibrary/Data/DbInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,16 @@ public static void Initialize(this IApplicationBuilder app)
var roleManager = services.GetRequiredService<RoleManager<Role>>();

// Apply any pending migrations
if (context.Database.GetPendingMigrations().Any())
try
{
context.Database.Migrate();
if (context.Database.GetPendingMigrations().Any())
{
context.Database.Migrate();
}
}
catch (Exception ex)
{
Console.WriteLine($"Migration error: {ex.Message}");
}

// Check if we already have games
Expand All @@ -42,7 +49,7 @@ public static void Initialize(this IApplicationBuilder app)
}

// Ensure roles are created
var roles = new[] { "Administrator", "User" };
var roles = new[] { "Administrator", "User", "Moderator" };
foreach (var role in roles)
{
if (!roleManager.RoleExistsAsync(role).Result)
Expand All @@ -68,6 +75,24 @@ public static void Initialize(this IApplicationBuilder app)
}
}

var moderatorUser = new User
{
Id = Guid.Parse("71a23c2d-f82e-4b47-a843-cfcadbd65a77"),
UserName = "[email protected]",
Email = "[email protected]",
EmailConfirmed = true
};

if (userManager.FindByNameAsync(moderatorUser.UserName).Result == null)
{
var result = userManager.CreateAsync(moderatorUser, "Password123!").Result;
if (result.Succeeded)
{
userManager.AddToRoleAsync(moderatorUser, "Moderator").Wait();
}
}


context.SaveChanges();

// Add test games
Expand Down
Binary file modified GameLibrary/Database.db-shm
Binary file not shown.
Binary file modified GameLibrary/Database.db-wal
Binary file not shown.
5 changes: 0 additions & 5 deletions GameLibrary/Pages/Account/Manage/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,5 @@
</div>
<button id="update-profile-button" type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
</form>

@if (Model.IsAdminRole)
{
<a asp-area="" asp-page="/Admin/Index">Admin Dashboard</a>
}
</div>
</div>
3 changes: 0 additions & 3 deletions GameLibrary/Pages/Account/Manage/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public IndexModel(
_signInManager = signInManager;
}

public bool IsAdminRole { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
Expand Down Expand Up @@ -92,8 +91,6 @@ public async Task<IActionResult> OnGetAsync()
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

IsAdminRole = _userManager.IsInRoleAsync(user, "Administrator").Result;

await LoadAsync(user);
return Page();
}
Expand Down
10 changes: 10 additions & 0 deletions GameLibrary/Pages/Account/Manage/ManageNavPages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public static class ManageNavPages
/// </summary>
public static string TwoFactorAuthentication => "TwoFactorAuthentication";

/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// </summary>
public static string RoleTools => "RoleTools";

/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
Expand Down Expand Up @@ -120,6 +125,11 @@ public static class ManageNavPages
/// </summary>
public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication);

/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// </summary>
public static string RoleToolsNavClass(ViewContext viewContext) => PageNavClass(viewContext, RoleTools);

/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
Expand Down
39 changes: 39 additions & 0 deletions GameLibrary/Pages/Account/Manage/RoleTools.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@page
@model RoleToolsModel
@{
ViewData["Title"] = "Role-Based Tools";
ViewData["ActivePage"] = ManageNavPages.RoleTools;
}

<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header bg-primary-subtle">
<h4 class="mb-0">Role-Based Tools</h4>
</div>
<div class="card-body">
<p class="text-muted">Access tools and dashboards based on your roles.</p>
<div class="d-grid gap-3">
@if (Model.IsAdminRole)
{
<a asp-area="" asp-page="/Admin/Index" class="btn btn-outline-primary btn-lg">
<i class="bi bi-gear-fill me-2"></i> Admin Dashboard
</a>
}
@if (Model.IsModeratorRole)
{
<a asp-area="" asp-page="/Moderator/Index" class="btn btn-outline-warning btn-lg">
<i class="bi bi-shield-fill me-2"></i> Moderator Dashboard
</a>
}
@if (!Model.IsAdminRole && !Model.IsModeratorRole)
{
<p class="text-muted">You do not have any role-based tools available.</p>
}
</div>
</div>
</div>
</div>
</div>
</div>
33 changes: 33 additions & 0 deletions GameLibrary/Pages/Account/Manage/RoleTools.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using GameLibrary.Models;

namespace GameLibrary.Pages.Account.Manage;

public class RoleToolsModel : PageModel
{
private readonly UserManager<User> _userManager;

public RoleToolsModel(UserManager<User> userManager)
{
_userManager = userManager;
}

public bool IsAdminRole { get; set; }
public bool IsModeratorRole { get; set; }

public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}

IsAdminRole = await _userManager.IsInRoleAsync(user, "Administrator");
IsModeratorRole = await _userManager.IsInRoleAsync(user, "Moderator");

return Page();
}
}
1 change: 1 addition & 0 deletions GameLibrary/Pages/Account/Manage/_ManageNav.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
}
<li class="nav-item"><a class="nav-link @ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)" id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
<li class="nav-item"><a class="nav-link @ManageNavPages.PersonalDataNavClass(ViewContext)" id="personal-data" asp-page="./PersonalData">Personal data</a></li>
<li class="nav-item"><a class="nav-link @ManageNavPages.RoleToolsNavClass(ViewContext)" id="role-tools" asp-page="./RoleTools">Role-Based Tools</a></li>
</ul>
55 changes: 55 additions & 0 deletions GameLibrary/Pages/Moderator/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@page
@model GameLibrary.Pages.Moderator.IndexModel

@{
ViewData["Title"] = "Moderator Index";
ViewData["ActivePage"] = ManageNavPages.Index;
}

<h1>Moderator Dashboard</h1>

<div class="row">
<div class="col-md-3">
<div class="card text-white bg-primary mb-3">
<div class="card-header">Total Reviews</div>
<div class="card-body">
<h5 class="card-title">@Model.TotalReviews</h5>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-info mb-3">
<div class="card-header">Total Users</div>
<div class="card-body">
<h5 class="card-title">@Model.TotalUsers</h5>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-6">
<h2>Review Ratings</h2>
<canvas id="reviewRatingsChart"></canvas>
</div>
</div>

@section Scripts {
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
var reviewRatingsCtx = document.getElementById('reviewRatingsChart').getContext('2d');
var reviewRatingsChart = new Chart(reviewRatingsCtx, {
type: 'bar',
data: {
labels: @Html.Raw(Json.Serialize(Model.ReviewRatings.Keys)),
datasets: [{
label: 'Review Ratings',
data: @Html.Raw(Json.Serialize(Model.ReviewRatings.Values)),
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
}]
}
});
</script>
}
30 changes: 30 additions & 0 deletions GameLibrary/Pages/Moderator/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using GameLibrary.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

namespace GameLibrary.Pages.Moderator;

public class IndexModel : PageModel
{
private readonly ApplicationDbContext _context;

public IndexModel(ApplicationDbContext context)
{
_context = context;
}

public int TotalReviews { get; set; }
public int TotalUsers { get; set; }

public Dictionary<int, int> ReviewRatings { get; set; } = new();

public async Task OnGetAsync()
{
TotalReviews = await _context.Reviews.CountAsync();
TotalUsers = await _context.Users.CountAsync();

ReviewRatings = await _context.Reviews
.GroupBy(r => r.Rating)
.ToDictionaryAsync(r => r.Key, r => r.Count());
}
}
21 changes: 21 additions & 0 deletions GameLibrary/Pages/Moderator/ManageNavPages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc.Rendering;

namespace GameLibrary.Pages.Moderator;

public static class ManageNavPages
{
public static string Index => "Index";
public static string Reviews => "Reviews/Index";
public static string Users => "Users/Index";

public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index);
public static string ReviewsNavClass(ViewContext viewContext) => PageNavClass(viewContext, Reviews);
public static string UsersNavClass(ViewContext viewContext) => PageNavClass(viewContext, Users);

public static string PageNavClass(ViewContext viewContext, string page)
{
var activePage = viewContext.ViewData["ActivePage"] as string
?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName);
return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null;
}
}
72 changes: 72 additions & 0 deletions GameLibrary/Pages/Moderator/Reviews/Edit.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@page
@model GameLibrary.Pages.Moderator.Reviews.EditModel

@{
ViewData["Title"] = "Edit Review";
}

<body style="color:#FFD43B;">

<header>
<h1>Edit Review</h1>
</header>

<div class="row container">
<div class="col-md-4">
@if (ViewData["Status"] != null)
{
<div class="alert alert-info">
Current Status: @ViewData["Status"]
</div>
}

<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>

<input type="hidden" asp-for="SelectedId" />

<div class="form-group">
<label asp-for="SelectedGameId" class="control-label">Game</label>
<select asp-for="SelectedGameId" class="form-control" asp-items="Model.Games">
<option value="">Select a game</option>
</select>
<span asp-validation-for="SelectedGameId" class="text-danger"></span>
</div>

<div class="form-group">
<label asp-for="SelectedUserId" class="control-label">User</label>
<select asp-for="SelectedUserId" class="form-control" asp-items="Model.Users">
<option value="">Select a user</option>
</select>
<span asp-validation-for="SelectedUserId" class="text-danger"></span>
</div>

<div class="form-group">
<label asp-for="SelectedRating" class="control-label">Rating</label>
<select asp-for="SelectedRating" class="form-select" required>
<option value="">Select rating</option>
<option value="5">5 - Excellent</option>
<option value="4">4 - Very Good</option>
<option value="3">3 - Good</option>
<option value="2">2 - Fair</option>
<option value="1">1 - Poor</option>
</select>
<span asp-validation-for="SelectedRating" class="text-danger"></span>
</div>

<div class="form-group">
<label asp-for="SelectedContent" class="control-label">Content</label>
<textarea asp-for="SelectedContent" class="form-control"></textarea>
<span asp-validation-for="SelectedContent" class="text-danger"></span>
</div>

<div class="form-group">
<input type="submit" name="submitButton" value="Approve" class="btn btn-success" style="background-color: green; color: white;" />
<input type="submit" name="submitButton" value="Deny" class="btn btn-danger" style="background-color: red; color: white;" />
<a asp-page="Index" class="btn btn-secondary" style="background-color: gray; color: white;">Back to List</a>
</div>
</form>
</div>
</div>

</body>
Loading

0 comments on commit 12a6d48

Please sign in to comment.