Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions GameLibrary/Controllers/GameDetailsApiController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using GameLibrary.Data;
using GameLibrary.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

namespace GameLibrary.Controllers;

[Route("api/games")]
[ApiController]
public class GameDetailsApiController : ControllerBase
{
private readonly ApplicationDbContext _context;

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

// GET: api/games/{id}
[HttpGet("{id}")]
public async Task<IActionResult> GetGame(Guid id)
{
var game = await _context.Games
.Include(g => g.Reviews)
.FirstOrDefaultAsync(g => g.Id == id);

if (game == null)
{
return NotFound();
}

return Ok(new
{
game.Id,
game.Title,
game.Description,
game.Developer,
game.Publisher,
game.ReleaseDate,
game.ImageUrl,
game.Rating,
ReviewCount = game.Reviews.Count
});
}

// GET: api/games/{id}/reviews
[HttpGet("{id}/reviews")]
public async Task<IActionResult> GetReviews(Guid id)
{
var reviews = await _context.Reviews
.Include(r => r.User)
.Where(r => r.GameId == id)
.OrderByDescending(r => r.CreatedAt)
.Select(r => new
{
r.Id,
r.Rating,
Content = r.Content,
UserName = r.User.UserName,
CreatedAt = r.CreatedAt
})
.ToListAsync();

return Ok(reviews);
}

// POST: api/games/{id}/reviews
[HttpPost("{id}/reviews")]
public async Task<IActionResult> CreateReview(Guid id, [FromBody] ReviewRequest request)
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (userId == null)
{
return Unauthorized();
}

var review = new Review
{
GameId = id,
UserId = Guid.Parse(userId),
Rating = request.Rating,
Content = request.Comment,
CreatedAt = DateTime.UtcNow
};

_context.Reviews.Add(review);
await _context.SaveChangesAsync();

// Recalculate average rating
var averageRating = await _context.Reviews
.Where(r => r.GameId == id)
.AverageAsync(r => r.Rating);

var game = await _context.Games.FindAsync(id);
if (game != null)
{
game.Rating = averageRating;
await _context.SaveChangesAsync();
}

// Return the new review with username
var reviewResponse = await _context.Reviews
.Include(r => r.User)
.Where(r => r.Id == review.Id)
.Select(r => new
{
r.Id,
r.Rating,
Content = r.Content,
UserName = r.User.UserName,
CreatedAt = r.CreatedAt,
GameRating = averageRating
})
.FirstAsync();

return Ok(reviewResponse);
}
}

public class ReviewRequest
{
public int Rating { get; set; }
public string Comment { get; set; } = string.Empty;
}
Binary file modified GameLibrary/Database.db
Binary file not shown.
112 changes: 59 additions & 53 deletions GameLibrary/Pages/Games/Details.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<link rel="stylesheet" href="~/css/Games/details.min.css" asp-append-version="true" />
}

<div class="alert alert-danger d-none" id="errorAlert"></div>
<div class="alert alert-success d-none" id="successAlert"></div>

<div class="container">
<div class="row">
<div class="col-md-4">
Expand All @@ -23,7 +26,7 @@
}
</div>
<div class="col-md-8">
<h1>@Model.Game.Title</h1>
<h1 id="gameTitle">@Model.Game.Title</h1>
<div class="mb-3">
<div class="game-rating">
@for (var i = 1; i <= 5; i++)
Expand All @@ -40,13 +43,13 @@
<span class="ms-2">(@Model.Game.Rating.ToString("F1"))</span>
</div>
</div>
<p class="lead">@Model.Game.Description</p>
<p class="lead" id="gameDescription">@Model.Game.Description</p>
<div class="game-details bg-dark p-3 rounded">
<div class="row">
<div class="col-md-6">
<p><strong>Developer:</strong> @Model.Game.Developer</p>
<p><strong>Publisher:</strong> @Model.Game.Publisher</p>
<p><strong>Release Date:</strong> @Model.Game.ReleaseDate.ToString("MMMM dd, yyyy")</p>
<p><strong>Developer:</strong> <span id="gameDeveloper">@Model.Game.Developer</span></p>
<p><strong>Publisher:</strong> <span id="gamePublisher">@Model.Game.Publisher</span></p>
<p><strong>Release Date:</strong> <span id="gameReleaseDate">@Model.Game.ReleaseDate.ToString("MMMM dd, yyyy")</span></p>
</div>
<div class="col-md-6">
<p><strong>Genre:</strong> @Model.Game.Genre</p>
Expand Down Expand Up @@ -108,59 +111,62 @@
<div class="card bg-dark">
<div class="card-body">
<h2>Reviews</h2>
<div class="review-stats mb-4">
<div class="average-rating">
<h3>Average Rating: @Model.AverageRating.ToString("F1")</h3>
<div class="rating-stars">
@for (var i = 1; i <= 5; i++)
{
if (Model.AverageRating >= i)
{
<i class="fas fa-star text-warning"></i>
}
else
{
<i class="far fa-star text-warning"></i>
}
}
</div>
<form id="reviewForm" method="post">
@Html.AntiForgeryToken()
<div class="mb-3">
<label for="Rating" class="form-label">Rating</label>
<select class="form-control" id="Rating" name="Rating" required>
<option value="">Select Rating...</option>
<option value="1">1 Star</option>
<option value="2">2 Stars</option>
<option value="3">3 Stars</option>
<option value="4">4 Stars</option>
<option value="5">5 Stars</option>
</select>
</div>
</div>

@if (!Model.Reviews.Any(r => r.UserId.ToString() == User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value))
{
<div class="add-review mb-4">
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#reviewForm">
Write a Review
</button>
<div class="collapse mt-3" id="reviewForm">
<div class="card card-body bg-dark">
<form method="post" asp-page-handler="AddReview" asp-route-id="@Model.Game.Id">
<div class="mb-3">
<label for="rating" class="form-label">Rating</label>
<select class="form-select" id="rating" name="rating" 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>
</div>
<div class="mb-3">
<label for="content" class="form-label">Review</label>
<textarea class="form-control" id="content" name="content" rows="3" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Review</button>
</form>
<div class="mb-3">
<label for="Comment" class="form-label">Comment</label>
<textarea class="form-control" id="Comment" name="Comment" rows="3" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Review</button>
</form>

<div id="reviewsList" class="mt-4">
@foreach (var review in Model.Reviews)
{
<div class="review mb-3">
<div class="d-flex justify-content-between">
<h5>@review.User.UserName</h5>
<div class="text-warning">
@for (var i = 1; i <= 5; i++)
{
if (review.Rating >= i)
{
<i class="fas fa-star"></i>
}
else
{
<i class="far fa-star"></i>
}
}
</div>
</div>
<p class="review-text">@review.Content</p>
<small class="text-muted">@review.CreatedAt.ToString("MMMM dd, yyyy")</small>
</div>
</div>
}

<partial name="_ReviewsList" model="@Model.Reviews" />
}
</div>
</div>
</div>
</div>
</div>
</div>

@section Scripts {
<script src="~/js/gameDetails.js" asp-append-version="true"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
gameDetails.init('@Model.Game.Id');
});
</script>
}
3 changes: 3 additions & 0 deletions GameLibrary/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public static void Main(string[] args)
options.Level = CompressionLevel.SmallestSize;
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseResponseCompression();
Expand All @@ -100,6 +102,7 @@ public static void Main(string[] args)
app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();
}
Expand Down
Loading
Loading