Add votes-final flag, warn on missing votes, and sync phases with results toggle

This commit is contained in:
2026-02-04 22:54:36 +01:00
parent 91692856f9
commit 13c8bb6194
13 changed files with 318 additions and 6 deletions

View File

@@ -19,6 +19,16 @@ public static class AdminEndpoints
state.ResultsOpen = request.ResultsOpen;
state.UpdatedAt = DateTimeOffset.UtcNow;
if (request.ResultsOpen)
{
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Results));
}
else
{
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Vote)
.SetProperty(x => x.VotesFinal, false));
}
await db.SaveChangesAsync();
var currentState = await db.AppState.AsNoTracking().FirstAsync();
return Results.Ok(new { currentState.ResultsOpen, currentState.UpdatedAt });
@@ -31,7 +41,8 @@ public static class AdminEndpoints
await db.Votes.ExecuteDeleteAsync();
await db.Suggestions.ExecuteDeleteAsync();
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Suggest));
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.CurrentPhase, Phase.Suggest)
.SetProperty(x => x.VotesFinal, false));
var state = await db.AppState.FirstAsync();
state.ResultsOpen = false;
state.UpdatedAt = DateTimeOffset.UtcNow;

View File

@@ -23,13 +23,26 @@ internal static class EndpointHelpers
var player = await db.Players.FirstOrDefaultAsync(p => p.Id == playerId);
if (player is null) return Phase.Suggest;
var state = await db.AppState.FirstAsync();
// Auto-upgrade any legacy Reveal phase to Vote to avoid blank screens
if (player.CurrentPhase == Phase.Reveal)
{
player.CurrentPhase = Phase.Vote;
await db.SaveChangesAsync();
}
// Keep phases aligned with results availability
if (state.ResultsOpen && player.CurrentPhase != Phase.Results)
{
player.CurrentPhase = Phase.Results;
}
else if (!state.ResultsOpen && player.CurrentPhase == Phase.Results)
{
player.CurrentPhase = Phase.Vote;
player.VotesFinal = false;
}
await db.SaveChangesAsync();
return player.CurrentPhase;
}

View File

@@ -20,6 +20,7 @@ public static class StateEndpoints
var summary = new
{
CurrentPhase = phase,
player.VotesFinal,
state.ResultsOpen,
state.UpdatedAt,
Players = await db.Players.CountAsync(),
@@ -34,7 +35,7 @@ public static class StateEndpoints
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
if (player is null) return Results.Unauthorized();
var phase = await EndpointHelpers.GetPhase(db, player.Id);
return Results.Ok(new { player.Id, player.DisplayName, player.Username, player.IsAdmin, CurrentPhase = phase });
return Results.Ok(new { player.Id, player.DisplayName, player.Username, player.IsAdmin, CurrentPhase = phase, player.VotesFinal });
});
app.MapPost("/api/me/phase/next", async (HttpContext ctx, AppDbContext db, IConfiguration config) =>
@@ -52,6 +53,7 @@ public static class StateEndpoints
}
player.CurrentPhase = next;
player.VotesFinal = false; // moving forward clears any prior finalize
await db.SaveChangesAsync();
return Results.Ok(new { player.CurrentPhase, appState.ResultsOpen });
});
@@ -67,6 +69,7 @@ public static class StateEndpoints
}
player.CurrentPhase = PrevPhase(player.CurrentPhase);
player.VotesFinal = false;
await db.SaveChangesAsync();
var appState = await db.AppState.AsNoTracking().FirstAsync();
return Results.Ok(new { player.CurrentPhase, appState.ResultsOpen });

View File

@@ -62,5 +62,18 @@ public static class VoteEndpoints
await db.SaveChangesAsync();
return Results.Ok(new { vote.Id, vote.Score });
});
app.MapPost("/api/votes/finalize", async ([FromBody] VoteFinalizeRequest request, HttpContext ctx, AppDbContext db) =>
{
var player = await EndpointHelpers.GetAuthenticatedPlayer(ctx, db);
if (player is null) return Results.Unauthorized();
var phase = await EndpointHelpers.GetPhase(db, player.Id);
if (phase != Phase.Vote)
return EndpointHelpers.PhaseMismatch(Phase.Vote, phase);
player.VotesFinal = request.Final;
await db.SaveChangesAsync();
return Results.Ok(new { player.VotesFinal });
});
}
}