Spaces:
Configuration error
Configuration error
File size: 4,764 Bytes
20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 66dca15 20817f1 028395c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Best Project – BOU 2026</title>
<style>
:root{
--bg:#f4f5f7; --card:#ffffff; --text:#1b1e24; --muted:#636a76;
--accent:#2f5ed1; --accent-bg:#e9efff; --border:#dfe2e8; --error:#c2302c;
}
@media (prefers-color-scheme: dark){
:root{
--bg:#14161b; --card:#1d2027; --text:#eef0f3; --muted:#9aa1ad;
--accent:#6e94f2; --accent-bg:#232a3d; --border:#2b2f38; --error:#ff8a84;
}
}
*{box-sizing:border-box;}
body{
margin:0; background:var(--bg); color:var(--text);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
}
.wrap{max-width:560px; margin:0 auto; padding:32px 18px 70px;}
h1{font-size:1.4rem; margin:0 0 2px;}
.sub{color:var(--muted); font-size:.9rem; margin:0 0 24px;}
section{margin-bottom:24px;}
label{display:block; font-size:.85rem; font-weight:600; margin-bottom:6px;}
input{
width:100%; padding:10px 12px; border-radius:10px; border:1px solid var(--border);
background:var(--card); color:var(--text); font-size:.95rem; margin-bottom:14px;
}
input:focus{outline:2px solid var(--accent); outline-offset:1px;}
.card{
background:var(--card); border:1px solid var(--border); border-radius:14px;
padding:14px 16px; margin-bottom:10px; cursor:pointer;
}
.card:hover{border-color:var(--accent);}
.card.selected{border-color:var(--accent); background:var(--accent-bg);}
.proj{font-weight:600; font-size:1rem;}
.presenter{color:var(--muted); font-size:.85rem; margin-top:2px;}
button{
width:100%; padding:13px; border:none; border-radius:10px; background:var(--accent);
color:#fff; font-size:1rem; font-weight:600; cursor:pointer;
}
button:disabled{opacity:.5; cursor:not-allowed;}
.msg{font-size:.88rem; margin-top:12px; min-height:1.2em;}
.msg.error{color:var(--error);}
.msg.ok{color:#1f9254;}
</style>
</head>
<body>
<div class="wrap">
<h1>Best Project — BOU 2026</h1>
<p class="sub">Enter your details, pick a project, then submit your vote.</p>
<section>
<label for="name">Name</label>
<input id="name" type="text" autocomplete="name" placeholder="Your full name">
<label for="uid">UID / Employee ID</label>
<input id="uid" type="text" autocomplete="off" placeholder="e.g. E123456">
<label for="email">Email</label>
<input id="email" type="email" autocomplete="email" placeholder="you@company.com">
</section>
<section id="projectList"></section>
<button id="submitBtn" disabled>Submit vote</button>
<div class="msg" id="msg"></div>
</div>
<script>
const nameEl = document.getElementById("name");
const uidEl = document.getElementById("uid");
const emailEl = document.getElementById("email");
const listEl = document.getElementById("projectList");
const btn = document.getElementById("submitBtn");
const msgEl = document.getElementById("msg");
let projects = [];
let selected = null;
function setMsg(text, kind){
msgEl.textContent = text || "";
msgEl.className = "msg" + (kind ? " " + kind : "");
}
function renderProjects(){
listEl.innerHTML = "";
projects.forEach(p => {
const div = document.createElement("div");
div.className = "card" + (selected === p.id ? " selected" : "");
div.innerHTML = `<div class="proj">${p.name}</div><div class="presenter">${p.who}</div>`;
div.addEventListener("click", () => { selected = p.id; renderProjects(); updateButton(); });
listEl.appendChild(div);
});
}
function updateButton(){
const valid = nameEl.value.trim() && uidEl.value.trim() &&
/^\S+@\S+\.\S+$/.test(emailEl.value.trim()) && selected;
btn.disabled = !valid;
}
[nameEl, uidEl, emailEl].forEach(el => el.addEventListener("input", updateButton));
fetch("/api/projects")
.then(r => r.json())
.then(data => { projects = data; renderProjects(); })
.catch(() => setMsg("Couldn't load the project list. Refresh the page.", "error"));
btn.addEventListener("click", async () => {
btn.disabled = true;
setMsg("Submitting…");
try{
const res = await fetch("/api/vote", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: nameEl.value.trim(),
uid: uidEl.value.trim(),
email: emailEl.value.trim(),
projectId: selected,
}),
});
const data = await res.json();
if (data.ok){
setMsg("Your vote has been recorded. Thank you!", "ok");
} else {
setMsg(data.error || "Something went wrong.", "error");
btn.disabled = false;
}
} catch (e){
setMsg("Network error — please try again.", "error");
btn.disabled = false;
}
});
</script>
</body>
</html>
|