I want to my button "Votar" insert data of vote in table of my database Votes, but when I click the button, nothing happens, doesn't show any error or something like this.
My structure of code is shown here, can you help me?
Database layer:
namespace ESOF.WebApp.DBLayer.Context;using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;public partial class ApplicationDbContext{ private void BuildGames(ModelBuilder modelBuilder) { modelBuilder.Entity<Game>() .HasKey(g => g.GameId); modelBuilder.Entity<Game>() .Property(g => g.GameId) .HasDefaultValueSql("gen_random_uuid()"); modelBuilder.Entity<Game>() .Property(g => g.Name) .IsRequired(); modelBuilder.Entity<Game>() .Property(g => g.Description) .IsRequired(); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;// ReSharper disable once CheckNamespacenamespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildPermissions(ModelBuilder modelBuilder) { modelBuilder.Entity<Permission>() .HasMany(p => p.RolePermissions) .WithOne(rp => rp.Permission) .HasForeignKey(rp => rp.PermissionId); modelBuilder.Entity<Permission>() .Property(p => p.PermissionId) .HasDefaultValueSql("gen_random_uuid()"); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;// ReSharper disable once CheckNamespacenamespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildRolePermissions(ModelBuilder modelBuilder) { modelBuilder.Entity<RolePermission>() .HasKey(e => new { e.RoleId, e.PermissionId }); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;// ReSharper disable once CheckNamespacenamespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildRoles(ModelBuilder modelBuilder) { modelBuilder.Entity<Role>() .HasMany(r => r.UserRoles) .WithOne(ur => ur.Role) .HasForeignKey(ur => ur.RoleId); modelBuilder.Entity<Role>() .HasMany(r => r.RolePermissions) .WithOne(rp => rp.Role) .HasForeignKey(rp => rp.RoleId); modelBuilder.Entity<Role>() .Property(p => p.RoleId) .HasDefaultValueSql("gen_random_uuid()"); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;// ReSharper disable once CheckNamespacenamespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildUserRoles(ModelBuilder modelBuilder) { modelBuilder.Entity<UserRole>() .HasKey(e => new { e.RoleId, e.UserId }); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;// ReSharper disable once CheckNamespacenamespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildUsers(ModelBuilder modelBuilder) { modelBuilder.Entity<User>() .HasMany(u => u.UserRoles) .WithOne(ur => ur.User) .HasForeignKey(ur => ur.UserId); modelBuilder.Entity<User>() .HasIndex(u => u.Email) .IsUnique(); modelBuilder.Entity<User>() .Property(p => p.UserId) .HasDefaultValueSql("gen_random_uuid()"); }}using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;namespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext{ private void BuildVotes(ModelBuilder modelBuilder) { modelBuilder.Entity<Vote>() .HasKey(e => e.VoteId); modelBuilder.Entity<Vote>() .Property(v => v.VoteId) .HasDefaultValueSql("gen_random_uuid()"); modelBuilder.Entity<Vote>() .Property(v => v.VoteTime) .IsRequired(); modelBuilder.Entity<Vote>() .Property(v => v.UserId) .IsRequired(); modelBuilder.Entity<Vote>() .Property(v => v.GameId) .IsRequired(); modelBuilder.Entity<Vote>() .HasOne(e => e.User) .WithMany(u => u.Votes) .HasForeignKey(e => e.UserId) .HasConstraintName("id_user__fk"); modelBuilder.Entity<Vote>() .HasOne(e => e.Game) .WithMany(g => g.Votes) .HasForeignKey(e => e.GameId) .HasConstraintName("id_game__fk"); }}using DotNetEnv;using ESOF.WebApp.DBLayer.Entities;using Helpers;using Microsoft.EntityFrameworkCore;namespace ESOF.WebApp.DBLayer.Context;public partial class ApplicationDbContext : DbContext{ private static readonly DbContextOptions DefaultOptions = new Func<DbContextOptions>(() => { var optionsBuilder = new DbContextOptionsBuilder(); var db = EnvFileHelper.GetString("POSTGRES_DB"); var user = EnvFileHelper.GetString("POSTGRES_USER"); var password = EnvFileHelper.GetString("POSTGRES_PASSWORD"); var port = EnvFileHelper.GetString("POSTGRES_PORT"); var host = EnvFileHelper.GetString("POSTGRES_HOST"); if (string.IsNullOrEmpty(db) || string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(port) || string.IsNullOrEmpty(host)) { throw new InvalidOperationException("Database connection information not fully specified in environment variables."); } var connectionString = $"Host={host};Port={port};Database={db};Username={user};Password={password}"; optionsBuilder.UseNpgsql(connectionString); return optionsBuilder.Options; })(); public ApplicationDbContext() : base(DefaultOptions) { } public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<User> Users { get; set; } public DbSet<Role> Roles { get; set; } public DbSet<Permission> Permissions { get; set; } public DbSet<UserRole> UserRoles { get; set; } public DbSet<RolePermission> RolePermissions { get; set; } public DbSet<Game> Games { get; set; } public DbSet<Vote> Votes { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); } protected override void OnModelCreating(ModelBuilder modelBuilder) { BuildUsers(modelBuilder); BuildRoles(modelBuilder); BuildPermissions(modelBuilder); BuildRolePermissions(modelBuilder); BuildUserRoles(modelBuilder); BuildGames(modelBuilder); BuildVotes(modelBuilder); base.OnModelCreating(modelBuilder); }}using System.ComponentModel.DataAnnotations;namespace ESOF.WebApp.DBLayer.Entities;public class Game{ [Key] public Guid GameId { get; set; } [Required] public String Name { get; set; } [Required] public String Description { get; set; } public ICollection<Vote> Votes { get; set; }}using System.ComponentModel.DataAnnotations;namespace ESOF.WebApp.DBLayer.Entities;public class Permission{ [Key] public Guid PermissionId { get; set; } [Required] public string Name { get; set; } public ICollection<RolePermission> RolePermissions { get; set; }}using System.ComponentModel.DataAnnotations;namespace ESOF.WebApp.DBLayer.Entities;public class Role{ [Key] public Guid RoleId { get; set; } [Required] public string Name { get; set; } public ICollection<UserRole> UserRoles { get; set; } public ICollection<RolePermission> RolePermissions { get; set; }}using System.ComponentModel.DataAnnotations;namespace ESOF.WebApp.DBLayer.Entities;public class RolePermission{ public Guid RoleId { get; set; } public Guid PermissionId { get; set; } public Role Role { get; set; } public Permission Permission { get; set; }}using System.ComponentModel.DataAnnotations;namespace ESOF.WebApp.DBLayer.Entities;public class User{ [Key] public Guid UserId { get; set; } [EmailAddress, Required] public string Email { get; set; } [Required] public byte[] PasswordHash { get; set; } [Required] public byte[] PasswordSalt { get; set; } public ICollection<UserRole> UserRoles { get; set; } public ICollection<Vote> Votes { get; set; }}namespace ESOF.WebApp.DBLayer.Entities;public class UserRole{ public Guid UserId { get; set; } public User User { get; set; } public Guid RoleId { get; set; } public Role Role { get; set; }}using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;namespace ESOF.WebApp.DBLayer.Entities;public class Vote{ [Key] public Guid VoteId { get; set; } [Required] public Guid UserId { get; set; } [ForeignKey(nameof(UserId))] public User User { get; set; } [Required] public Guid GameId { get; set; } [ForeignKey(nameof(GameId))] public Game Game { get; set; } [Required] public DateTime VoteTime { get; set; }}Frontend
@page "/gameofthemonth"@using ESOF.WebApp.DBLayer.Entities@inject HttpClient Http<h3>Vote no Jogo do Mês</h3>@if (games == null){<p>Carregando...</p>}else if (!games.Any()){<p>Nenhum jogo encontrado.</p>}else{<ul> @foreach (var game in games) {<li><b>@game.Name</b> - @game.Description<button @onclick="@(async () => await VoteForGame(userId, game.GameId))" disabled="@hasVoted">Votar</button> @if (votePercentages.TryGetValue(game.GameId, out var percentage)) {<span>(@percentage%)</span> }</li> }</ul>}<p>@message</p>@code { private List<Game>? games; private string? message; private Guid userId = Guid.NewGuid(); private bool hasVoted = false; private Dictionary<Guid, double> votePercentages = new(); private bool isVotingPeriod = false; protected override async Task OnInitializedAsync() { try { games = await Http.GetFromJsonAsync<List<Game>>("api/game"); hasVoted = await CheckIfUserHasVoted(); isVotingPeriod = IsVotingPeriod(); if (games == null) { message = "Nenhum jogo foi retornado da API."; } else { await UpdateVotePercentages(); } } catch (Exception ex) { message = "Erro ao carregar os jogos: " + ex.Message; } } private bool IsVotingPeriod() { var today = DateTime.Now; var lastDayOfMonth = new DateTime(today.Year, today.Month, DateTime.DaysInMonth(today.Year, today.Month)); var firstVotingDay = lastDayOfMonth.AddDays(-4); return today >= firstVotingDay; } private async Task<bool> CheckIfUserHasVoted() { return await Http.GetFromJsonAsync<bool>($"api/vote/HasVoted/{userId}"); } private async Task VoteForGame(Guid userId, Guid gameId) { if (!isVotingPeriod) { message = "A votação só está disponível nos últimos 5 dias do mês."; return; } try { if (await CheckIfUserHasVoted()) { message = "Você já votou neste mês."; return; } var vote = new Vote { UserId = userId, GameId = gameId, VoteTime = DateTime.Now }; var response = await Http.PostAsJsonAsync("api/vote", vote); if (response.IsSuccessStatusCode) { message = "Voto registrado com sucesso!"; hasVoted = true; await UpdateVotePercentages(); } else { var errorContent = await response.Content.ReadAsStringAsync(); message = errorContent.Contains("Já votou este mês.") ? "Já votou este mês." : "Erro ao registrar voto."; } } catch (Exception ex) { message = "Erro ao tentar registrar o voto: " + ex.Message; } StateHasChanged(); } private async Task UpdateVotePercentages() { var voteCounts = await Http.GetFromJsonAsync<Dictionary<Guid, int>>("api/vote/Counts"); if (voteCounts != null) { var totalVotes = voteCounts.Values.Sum(); foreach (var game in games) { votePercentages[game.GameId] = voteCounts.TryGetValue(game.GameId, out var count) ? (double)count / totalVotes * 100 : 0; } } }}@page "/votes"@inject HttpClient Http@using ESOF.WebApp.DBLayer.Entities<h3>Jogo do Mês</h3>@if (gameOfTheMonth != null){<div><b>@gameOfTheMonth.Name</b><p>@gameOfTheMonth.Description</p></div>}else{<p>Nenhum jogo foi selecionado este mês.</p>}@code { private Game? gameOfTheMonth; protected override async Task OnInitializedAsync() { try { gameOfTheMonth = await Http.GetFromJsonAsync<Game>("api/vote/GameOfTheMonth"); } catch (Exception ex) { Console.WriteLine("Erro ao carregar o jogo do mês: " + ex.Message); } }}namespace Frontend.Helpers;public class ApiHelper(HttpClient httpClient){ public async Task<T?> GetFromApiAsync<T>(string url) { try { var response = await httpClient.GetAsync(url); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<T>(); } catch (HttpRequestException e) { // Handle exception throw new ApplicationException($"Error fetching data from {url}: {e.Message}"); } }}using ESOF.WebApp.DBLayer.Entities;using System.Net.Http;using System.Net.Http.Json;using System.Threading.Tasks;using System.Collections.Generic;namespace Frontend.Services{ public class VoteService { private readonly HttpClient _httpClient; public VoteService(HttpClient httpClient) { _httpClient = httpClient; } public async Task<List<Game>> GetGamesAsync() { return await _httpClient.GetFromJsonAsync<List<Game>>("api/game"); } public async Task<bool> HasUserVotedAsync(Guid userId) { return await _httpClient.GetFromJsonAsync<bool>($"api/vote/HasVoted/{userId}"); } public async Task<HttpResponseMessage> VoteForGameAsync(Vote vote) { return await _httpClient.PostAsJsonAsync("api/vote", vote); } public async Task<Dictionary<Guid, int>> GetVoteCountsAsync() { return await _httpClient.GetFromJsonAsync<Dictionary<Guid, int>>("api/vote/Counts"); } }}Web API:
using Microsoft.AspNetCore.Mvc;using ESOF.WebApp.DBLayer.Entities;using ESOF.WebApp.DBLayer.Context;using Microsoft.EntityFrameworkCore;[Route("api/[controller]")][ApiController]public class GameController : ControllerBase{ private readonly ApplicationDbContext _context; public GameController(ApplicationDbContext context) { _context = context; } [HttpGet] public async Task<ActionResult<IEnumerable<Game>>> GetGames() { try { var games = await _context.Games.ToListAsync(); return Ok(games); } catch (Exception ex) { // Log exception Console.WriteLine($"Erro ao buscar jogos: {ex.Message}"); return StatusCode(500, "Erro interno no servidor."); } } [HttpGet("{id}")] public async Task<ActionResult<Game>> GetGame(Guid id) { try { var game = await _context.Games.FindAsync(id); if (game == null) { return NotFound(); } return game; } catch (Exception ex) { // Log exception Console.WriteLine($"Erro ao procurar jogo: {ex.Message}"); return StatusCode(500, "Erro interno no servidor."); } }}using Microsoft.AspNetCore.Mvc;using System;using System.Linq;using System.Threading.Tasks;using ESOF.WebApp.DBLayer.Context;using ESOF.WebApp.DBLayer.Entities;using Microsoft.EntityFrameworkCore;[Route("api/[controller]")][ApiController]public class VoteController : ControllerBase{ private readonly ApplicationDbContext _context; public VoteController(ApplicationDbContext context) { _context = context; } [HttpPost] public async Task<IActionResult> Vote([FromBody] Vote vote) { var today = DateTime.Now; var lastDayOfMonth = new DateTime(today.Year, today.Month, DateTime.DaysInMonth(today.Year, today.Month)); var firstVotingDay = lastDayOfMonth.AddDays(-4); if (today < firstVotingDay) { return BadRequest("A votação só está disponível nos últimos 5 dias do mês."); } try { var existingVote = await _context.Votes .FirstOrDefaultAsync(v => v.UserId == vote.UserId && v.VoteTime.Month == today.Month && v.VoteTime.Year == today.Year); if (existingVote != null) { return BadRequest("Você já votou este mês."); } vote.VoteTime = today; _context.Votes.Add(vote); await _context.SaveChangesAsync(); return Ok(); } catch (Exception ex) { Console.WriteLine($"Erro ao registrar voto: {ex.Message}"); return StatusCode(500, "Erro interno no servidor."); } } [HttpGet("GameOfTheMonth")] public async Task<ActionResult<Game>> GetGameOfTheMonth() { try { var gameOfTheMonthId = await _context.Votes .Where(v => v.VoteTime.Month == DateTime.Now.Month && v.VoteTime.Year == DateTime.Now.Year) .GroupBy(v => v.GameId) .OrderByDescending(g => g.Count()) .Select(g => g.Key) .FirstOrDefaultAsync(); if (gameOfTheMonthId == default) { return NotFound("Nenhum voto foi registado este mês."); } var game = await _context.Games.FindAsync(gameOfTheMonthId); return Ok(game); } catch (Exception ex) { Console.WriteLine($"Erro ao procurar jogo do mês: {ex.Message}"); return StatusCode(500, "Erro interno no servidor."); } } [HttpGet("HasVoted/{userId}")] public async Task<ActionResult<bool>> HasVoted(Guid userId) { var existingVote = await _context.Votes .AnyAsync(v => v.UserId == userId && v.VoteTime.Month == DateTime.Now.Month && v.VoteTime.Year == DateTime.Now.Year); return Ok(existingVote); } [HttpGet("Counts")] public async Task<ActionResult<Dictionary<Guid, int>>> GetVoteCounts() { var voteCounts = await _context.Votes .Where(v => v.VoteTime.Month == DateTime.Now.Month && v.VoteTime.Year == DateTime.Now.Year) .GroupBy(v => v.GameId) .Select(g => new { GameId = g.Key, Count = g.Count() }) .ToDictionaryAsync(g => g.GameId, g => g.Count); return Ok(voteCounts); }}using ESOF.WebApp.DBLayer.Entities;using ESOF.WebApp.DBLayer.Context;using Microsoft.EntityFrameworkCore;using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace ESOF.WebApp.WebAPI.Services{ public class VoteService { private readonly ApplicationDbContext _context; public VoteService(ApplicationDbContext context) { _context = context; } public async Task<List<Game>> GetGamesAsync() { return await _context.Games.ToListAsync(); } private async Task<bool> HasUserVotedAsync(Guid userId, int month, int year) { return await _context.Votes .AnyAsync(v => v.UserId == userId && v.VoteTime.Month == month && v.VoteTime.Year == year); } public async Task<bool> VoteForGameAsync(Vote vote) { // Verifica se o usuário já votou neste mês (se necessário) var hasVoted = await HasUserVotedAsync(vote.UserId, vote.VoteTime.Month, vote.VoteTime.Year); if (hasVoted) { return false; // Ou lance uma exceção informando que o usuário já votou } _context.Votes.Add(vote); var result = await _context.SaveChangesAsync() > 0; return result; } public async Task<Dictionary<Guid, int>> GetVoteCountsAsync() { return await _context.Votes .GroupBy(v => v.GameId) .Select(g => new { GameId = g.Key, Count = g.Count() }) .ToDictionaryAsync(g => g.GameId, g => g.Count); } public async Task<Dictionary<Guid, double>> GetVotePercentagesAsync() { var totalVotes = await _context.Votes.CountAsync(); if (totalVotes == 0) return new Dictionary<Guid, double>(); return await _context.Votes .GroupBy(v => v.GameId) .Select(g => new { GameId = g.Key, Percentage = (double)g.Count() / totalVotes * 100 }) .ToDictionaryAsync(g => g.GameId, g => g.Percentage); } public async Task<Game?> GetGameOfTheMonthAsync() { var gameOfTheMonthId = await _context.Votes .Where(v => v.VoteTime.Month == DateTime.Now.Month && v.VoteTime.Year == DateTime.Now.Year) .GroupBy(v => v.GameId) .OrderByDescending(g => g.Count()) .Select(g => g.Key) .FirstOrDefaultAsync(); if (gameOfTheMonthId == Guid.Empty) { return null; } var game = await _context.Games.FindAsync(gameOfTheMonthId); return game; } }}After that code I need make polls with all games, the game what have bigger number of votes will be Game of The Month, the poll will open in the last 5 days of the month and refresh the game of the month in day 1 of the next month.
Can you help me?