180 lines
6.4 KiB
JavaScript
180 lines
6.4 KiB
JavaScript
import { t } from "./i18n.js";
|
|
import { state } from "./state.js";
|
|
import { $ } from "./dom.js";
|
|
import { buildLinkOptionLabel, truncate } from "./ui-utils.js";
|
|
|
|
function displayPlayerStatus(player) {
|
|
if (!player) return "";
|
|
const phase = player.phase;
|
|
if (phase === "Suggest") return t("admin.statusSuggesting");
|
|
if (phase === "Vote")
|
|
return player.finalized
|
|
? t("admin.statusFinished")
|
|
: t("admin.statusVoting");
|
|
if (phase === "Results") return t("admin.statusFinished");
|
|
return phase;
|
|
}
|
|
|
|
function buildStatusSelect(player) {
|
|
const canMoveToSuggest = player.phase === "Vote";
|
|
const select = document.createElement("select");
|
|
select.className = "chip admin-status-select";
|
|
select.dataset.setPlayerPhase = player.playerId;
|
|
select.setAttribute("aria-label", t("admin.playerStatus"));
|
|
|
|
const current = document.createElement("option");
|
|
current.value = "";
|
|
current.selected = true;
|
|
current.textContent = displayPlayerStatus(player);
|
|
|
|
const suggest = document.createElement("option");
|
|
suggest.value = "Suggest";
|
|
suggest.disabled = !canMoveToSuggest;
|
|
suggest.textContent = t("admin.statusMoveToSuggest");
|
|
|
|
select.append(current, suggest);
|
|
return select;
|
|
}
|
|
|
|
export function renderAdminVoteStatus() {
|
|
if (!state.me?.isAdmin) return;
|
|
if (state.adminStatusSelectActive) return;
|
|
const statusBadge = $("admin-ready-status");
|
|
const table = $("admin-player-table")?.querySelector("tbody");
|
|
if (!state.adminVoteStatus || !statusBadge || !table) return;
|
|
|
|
table.innerHTML = "";
|
|
state.adminVoteStatus.voters.forEach((v) => {
|
|
const tr = document.createElement("tr");
|
|
const gamesTooltip = (v.suggestionTitles || []).join(", ");
|
|
|
|
const nameCell = document.createElement("td");
|
|
nameCell.title = v.name ?? "";
|
|
nameCell.textContent = truncate(v.name, 28);
|
|
|
|
const usernameCell = document.createElement("td");
|
|
usernameCell.className = "muted small";
|
|
usernameCell.title = v.username ?? "";
|
|
usernameCell.textContent = truncate(v.username, 24);
|
|
|
|
const statusCell = document.createElement("td");
|
|
statusCell.appendChild(buildStatusSelect(v));
|
|
|
|
const countCell = document.createElement("td");
|
|
countCell.title = gamesTooltip;
|
|
countCell.textContent = String(v.suggestionCount ?? 0);
|
|
|
|
const jokerCell = document.createElement("td");
|
|
const jokerButton = document.createElement("button");
|
|
jokerButton.className = "chip";
|
|
jokerButton.dataset.grantJoker = v.playerId;
|
|
jokerButton.type = "button";
|
|
jokerButton.textContent = v.hasJoker ? "🎟" : t("admin.grantJokerChip");
|
|
jokerCell.appendChild(jokerButton);
|
|
|
|
const adminCell = document.createElement("td");
|
|
if (v.isOwner) {
|
|
const ownerLabel = document.createElement("span");
|
|
ownerLabel.className = "muted small";
|
|
ownerLabel.textContent = t("admin.owner");
|
|
adminCell.appendChild(ownerLabel);
|
|
} else {
|
|
const adminCheckbox = document.createElement("input");
|
|
adminCheckbox.type = "checkbox";
|
|
adminCheckbox.dataset.setPlayerAdmin = v.playerId;
|
|
adminCheckbox.checked = !!v.isAdmin;
|
|
adminCheckbox.setAttribute("aria-label", t("admin.playerAdmin"));
|
|
adminCell.appendChild(adminCheckbox);
|
|
}
|
|
|
|
const deleteCell = document.createElement("td");
|
|
const deleteButton = document.createElement("button");
|
|
deleteButton.className = "chip danger-chip";
|
|
deleteButton.dataset.deletePlayer = v.playerId;
|
|
deleteButton.dataset.name = v.name ?? "";
|
|
deleteButton.type = "button";
|
|
deleteButton.textContent = "✕";
|
|
deleteCell.appendChild(deleteButton);
|
|
|
|
tr.append(
|
|
nameCell,
|
|
usernameCell,
|
|
statusCell,
|
|
countCell,
|
|
jokerCell,
|
|
adminCell,
|
|
deleteCell,
|
|
);
|
|
table.appendChild(tr);
|
|
});
|
|
|
|
const waiting = state.adminVoteStatus.waiting;
|
|
const ready = waiting.length === 0;
|
|
const waitingDisplay = waiting.map((name) =>
|
|
name?.length > 24 ? `${name.slice(0, 21)}...` : name,
|
|
);
|
|
statusBadge.textContent = ready
|
|
? t("admin.readyForResults")
|
|
: t("admin.waitingForPlayers", { names: waitingDisplay.join(", ") });
|
|
statusBadge.className = ready ? "badge" : "badge warning";
|
|
}
|
|
|
|
export function renderAdminLinker() {
|
|
const wrap = $("admin-linker");
|
|
const source = $("link-source");
|
|
const target = $("link-target");
|
|
if (!wrap || !source || !target) return;
|
|
|
|
const visible = state.me?.isAdmin && state.phase === "Vote";
|
|
wrap.classList.toggle("hidden", !visible);
|
|
if (!visible) return;
|
|
|
|
const previousSource = source.value;
|
|
const previousTarget = target.value;
|
|
const options = (state.allSuggestions ?? [])
|
|
.slice()
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
const fillSelect = (select, placeholderKey) => {
|
|
select.innerHTML = "";
|
|
const placeholder = document.createElement("option");
|
|
placeholder.value = "";
|
|
placeholder.textContent = t(placeholderKey);
|
|
placeholder.disabled = true;
|
|
placeholder.selected = true;
|
|
select.appendChild(placeholder);
|
|
|
|
options.forEach((s) => {
|
|
const opt = document.createElement("option");
|
|
opt.value = s.id;
|
|
opt.textContent = buildLinkOptionLabel(s);
|
|
select.appendChild(opt);
|
|
});
|
|
};
|
|
|
|
fillSelect(source, "admin.linkSourcePlaceholder");
|
|
fillSelect(target, "admin.linkTargetPlaceholder");
|
|
|
|
if (previousSource && options.some((s) => String(s.id) === previousSource))
|
|
source.value = previousSource;
|
|
if (previousTarget && options.some((s) => String(s.id) === previousTarget))
|
|
target.value = previousTarget;
|
|
|
|
const preventSameSelection = () => {
|
|
const sourceVal = source.value;
|
|
const targetVal = target.value;
|
|
Array.from(target.options).forEach((opt) => {
|
|
if (!opt.value) return;
|
|
opt.disabled = opt.value === sourceVal;
|
|
});
|
|
Array.from(source.options).forEach((opt) => {
|
|
if (!opt.value) return;
|
|
opt.disabled = opt.value === targetVal;
|
|
});
|
|
};
|
|
|
|
source.onchange = preventSameSelection;
|
|
target.onchange = preventSameSelection;
|
|
preventSameSelection();
|
|
}
|