(async function () { const root = document.getElementById("app"); const user = await bootstrapAuth({ requireAuth: true, forbidIfMustChange: true }); if (!user) return; const state = { competitions: [], filter: "all" }; async function loadCompetitions() { state.competitions = await API.listCompetitions(); } async function openCompetitionModal() { const backdrop = el("div", { class: "modal-backdrop", onclick: (e) => { if (e.target === backdrop) backdrop.remove(); } }); const nameInput = el("input", { type: "text" }); const allowInput = el("input", { type: "checkbox" }); let langs = I18N_AVAILABLE; try { const fromApi = await API.listRuleLanguages(); if (fromApi && fromApi.length) langs = [...fromApi].sort(); } catch (e) {} const defaultLang = langs.includes(user.language) ? user.language : "en"; const langInput = el("select", null, ...langs.map((l) => el("option", { value: l, selected: l === defaultLang }, I18N_NAMES[l] || l)) ); backdrop.appendChild(el("div", { class: "modal" }, el("h3", null, t("new_competition")), el("div", { class: "field" }, el("label", null, t("competition_name")), nameInput), el("div", { class: "field" }, el("label", null, t("rules_language")), langInput, el("div", { class: "muted small" }, t("rules_language_hint"))), el("label", { class: "row" }, allowInput, " " + t("allow_any_scorer_edit")), el("div", { class: "row", style: { justifyContent: "flex-end", marginTop: "1rem" } }, el("button", { onclick: () => backdrop.remove() }, t("cancel")), el("button", { class: "primary", onclick: async () => { if (!nameInput.value.trim()) return; try { await API.createCompetition({ name: nameInput.value.trim(), allow_any_scorer_edit: allowInput.checked, rules_language: langInput.value, }); backdrop.remove(); await loadCompetitions(); render(); } catch (e) { alert(e.message); } } }, t("create")), ), )); document.body.appendChild(backdrop); } function openCardMenu(anchorEl, c) { openInlineMenu(anchorEl, [ { label: t("delete"), danger: true, onClick: async () => { if (!confirm(t("confirm_delete_competition_named", { name: c.name }))) return; try { await API.deleteCompetition(c.id); await loadCompetitions(); render(); } catch (e) { alert((e.data && e.data.error) || e.message); } } }, ]); } function matchesFilter(c) { if (state.filter === "open") return !c.closed; if (state.filter === "closed") return !!c.closed; return true; } function render() { clearNode(root); root.appendChild(renderTopbar(user)); const container = el("div", { class: "container" }); const filterSelect = el("select", { onchange: (e) => { state.filter = e.target.value; render(); } }, el("option", { value: "all", selected: state.filter === "all" }, t("filter_all")), el("option", { value: "open", selected: state.filter === "open" }, t("open")), el("option", { value: "closed", selected: state.filter === "closed" }, t("closed")), ); container.appendChild(el("div", { class: "row", style: { justifyContent: "space-between", marginBottom: "0.75rem" } }, el("h2", { style: { margin: 0 } }, t("competitions")), el("div", { class: "row" }, filterSelect, user.is_system_admin && el("button", { class: "primary", onclick: openCompetitionModal }, t("new_competition")), ), )); const filtered = state.competitions.filter(matchesFilter); if (filtered.length === 0) { container.appendChild(el("div", { class: "muted" }, t("no_competitions"))); } else { const grid = el("div", { class: "grid" }); for (const c of filtered) { const menuBtn = user.is_system_admin ? el("button", { class: "ghost action-btn", title: t("actions"), onclick: (e) => { e.stopPropagation(); openCardMenu(e.currentTarget, c); } }, "⋮") : null; grid.appendChild(el("div", { class: "card clickable", tabindex: "0", onclick: () => navigate("competition", { id: c.id }), onkeydown: (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); navigate("competition", { id: c.id }); } }, }, el("div", { class: "row", style: { justifyContent: "space-between", alignItems: "center", flexWrap: "nowrap" } }, el("h2", { title: c.name, style: { margin: 0, minWidth: 0, flex: "1", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, c.name), menuBtn && el("div", { style: { flexShrink: "0" } }, menuBtn), ), el("div", { class: "muted", style: { marginTop: "0.5rem" } }, el("span", { class: "badge accent" }, t(c.role)), c.closed ? " " : null, c.closed ? el("span", { class: "badge warn" }, t("closed")) : null, ), )); } container.appendChild(grid); } root.appendChild(container); } await loadCompetitions(); render(); })();