Require display name before suggesting/voting and enforce in UI

This commit is contained in:
2026-01-28 16:30:30 +01:00
parent e49fb04e3a
commit 6396d583b5
3 changed files with 38 additions and 0 deletions

View File

@@ -178,6 +178,11 @@ api.MapPost("/suggestions", async ([FromBody] SuggestionRequest request, HttpCon
var player = await GetOrCreatePlayer(ctx, db); var player = await GetOrCreatePlayer(ctx, db);
if (string.IsNullOrWhiteSpace(player.DisplayName))
{
return Results.BadRequest(new { error = "Set a display name before submitting suggestions." });
}
var existingCount = await db.Suggestions.CountAsync(s => s.PlayerId == player.Id); var existingCount = await db.Suggestions.CountAsync(s => s.PlayerId == player.Id);
if (existingCount >= 3) if (existingCount >= 3)
{ {
@@ -263,6 +268,9 @@ api.MapPost("/votes", async ([FromBody] VoteRequest request, HttpContext ctx, Ap
var player = await GetOrCreatePlayer(ctx, db); var player = await GetOrCreatePlayer(ctx, db);
if (string.IsNullOrWhiteSpace(player.DisplayName))
return Results.BadRequest(new { error = "Set a display name before voting." });
var suggestionExists = await db.Suggestions.AnyAsync(s => s.Id == request.SuggestionId); var suggestionExists = await db.Suggestions.AnyAsync(s => s.Id == request.SuggestionId);
if (!suggestionExists) if (!suggestionExists)
return Results.BadRequest(new { error = "Suggestion not found." }); return Results.BadRequest(new { error = "Suggestion not found." });

View File

@@ -55,6 +55,7 @@ async function loadState() {
if ($("player-id")) { if ($("player-id")) {
$("player-id").textContent = `Player ID: ${me.id}`; $("player-id").textContent = `Player ID: ${me.id}`;
} }
applyNameRequirementUI();
} }
async function loadSuggestData() { async function loadSuggestData() {
@@ -97,6 +98,7 @@ function renderPhasePill() {
if (phaseSelect && !phaseSelect.dataset.userEditing) { if (phaseSelect && !phaseSelect.dataset.userEditing) {
phaseSelect.value = state.phase || "Suggest"; phaseSelect.value = state.phase || "Suggest";
} }
applyNameRequirementUI();
} }
function renderCounts() { function renderCounts() {
@@ -216,6 +218,7 @@ function setupHandlers() {
state.me = me; state.me = me;
nameInput.dataset.userEditing = ""; nameInput.dataset.userEditing = "";
toast("Saved name"); toast("Saved name");
applyNameRequirementUI();
} catch (err) { } catch (err) {
toast(err.message, true); toast(err.message, true);
} }
@@ -335,6 +338,31 @@ function openLightbox(url, title) {
document.body.appendChild(overlay); document.body.appendChild(overlay);
} }
function applyNameRequirementUI() {
const requiresName = !state.me?.displayName?.trim();
const warning = $("name-warning");
if (warning) warning.classList.toggle("hidden", !requiresName);
const suggestForm = $("suggest-form");
if (suggestForm) {
suggestForm.querySelectorAll("input,textarea,button").forEach(el => {
if (el.id === "save-name") return;
el.disabled = requiresName;
});
suggestForm.classList.toggle("disabled-form", requiresName);
}
const voteList = $("vote-list");
if (voteList) {
voteList.querySelectorAll("input[type=range]").forEach(el => el.disabled = requiresName);
voteList.classList.toggle("disabled-form", requiresName);
}
if (requiresName && state.phase !== "Suggest") {
toast("Enter a name to continue.", true);
}
}
async function main() { async function main() {
setupHandlers(); setupHandlers();
try { try {

View File

@@ -106,6 +106,8 @@ button.ghost {
.label { color: #9ca3af; font-size: 12px; } .label { color: #9ca3af; font-size: 12px; }
.hint { color: #9ca3af; font-size: 12px; margin: 8px 0 0; } .hint { color: #9ca3af; font-size: 12px; margin: 8px 0 0; }
.hint.warning { color: #f59e0b; }
.disabled-form { opacity: 0.5; pointer-events: none; }
.card-grid { .card-grid {
display: grid; display: grid;