1import * as api from "../api.js"; 2import * as storage from "../storage.js"; 3import * as ui from "../ui-common.js"; 4import { normalizeName } from "../names.js"; 5const defaultDeps = { api, storage, ui }; 6 7const ROSTER_ROWS = [["yes", "⯠åå "], ["maybe", "â³ æªå®"], ["no", "à ä¸åå "]]; 8 9// ç»é¢2: åºæ¬ 調æ´ï¼ã¡ã³ãã¼ï¼ãprops: { event, attendances, identity, groupToken, navigate } 10export function mount(container, props, deps = defaultDeps) { 11 const ev = props.event; 12 const id = props.identity || { success: false }; 13 // â ãã¾ã®åçï¼â¯â³Ãï¼ã¯ã¡ã³ãã¼ã«ãè¦ããï¼å®æ©FB 2026-09-15ï¼ãðã®ç¡ã端æ«ã§éããå¹¹äºã 14 // ããã«æ¥ãã®ã§ãè¦ããªãã¨èª°ãåå ããã®ãåãããªããget-event ã¯å ããå ¨å¡ã«è¿ãã¦ããã 15 let atts = props.attendances || []; 16 17 // åãååï¼normalizeNameï¼ã®äºéç»é²ã¯1人ã¨ãã¦æ°ãããå½ãã¼ã«ã®äººã®åå®ã¯å ã ååãã¼ 18 // ï¼ã°ã«ã¼ãæ¦ç¸¾ãååã1人ã¨ãã¦éè¨ï¼ãªã®ã§ãããã«æããã 19 function uniqueNames(rows) { 20 const seen = new Set(); 21 const out = []; 22 for (const a of rows) { 23 const name = (a.player && a.player.display_name) || ""; 24 const key = normalizeName(name); 25 if (!key || seen.has(key)) continue; 26 seen.add(key); 27 out.push(name); 28 } 29 return out; 30 } 31 32 function roster() { 33 const box = document.createElement("div"); 34 box.id = "att-roster"; 35 box.setAttribute("style", "margin: 8px 0"); 36 const line = (text) => { 37 const d = document.createElement("div"); 38 d.textContent = text; 39 box.appendChild(d); 40 }; 41 line("ð¥ ãã¾ã®åç"); 42 if (atts.length === 0) { 43 line("ã¾ã åçãããã¾ãã"); 44 return box; 45 } 46 for (const [answer, label] of ROSTER_ROWS) { 47 const names = uniqueNames(atts.filter((a) => a.answer === answer)); 48 line(names.length ? `${label} ${names.length}å: ${names.join(" / ")}` : `${label} 0å`); 49 } 50 const drivers = uniqueNames(atts.filter((a) => a.answer === "yes" && a.can_drive)).length; 51 line(`ð è»åºãå¯è½ï¼åå è ï¼: ${drivers}å`); 52 return box; 53 } 54 55 function renderForm() { 56 container.innerHTML = ""; 57 58 const head = document.createElement("div"); 59 head.className = "card"; 60 const org = (ev.organizer && ev.organizer.display_name) || ""; 61 const orgGuide = org ? `å¹¹äºï¼${escapeHtml(org)}ï¼ã«å±ãã¾ã` : "å¹¹äºã«å±ãã¾ã"; 62 head.innerHTML = `<div class="subhead">${escapeHtml(ev.name || "ã©ã¦ã³ãåºæ¬ ")}</div>` + 63 `<div>å¹¹äº: ${escapeHtml(org || "-")}</div>` + 64 `<div>ãã¬ã¼æ¥: ${escapeHtml(ev.play_date || "-")}</div>`; 65 head.appendChild(roster()); 66 const guide = document.createElement("p"); 67 guide.className = "notice"; 68 guide.innerHTML = `ð¡ åçããã¨${orgGuide}ã`; // orgGuide ã®å¹¹äºå㯠escapeHtml æ¸ã¿ 69 head.appendChild(guide); 70 container.appendChild(head); 71 72 const card = document.createElement("div"); 73 card.className = "card"; 74 75 // ååï¼LIFF å徿¸ãªãåæå¤ï¼ 76 const nameWrap = document.createElement("div"); 77 nameWrap.innerHTML = `<label for="att-name">ãåå</label>`; 78 const nameInput = document.createElement("input"); 79 nameInput.id = "att-name"; 80 nameInput.value = id.success ? (id.display_name || "") : ""; 81 nameWrap.appendChild(nameInput); 82 card.appendChild(nameWrap); 83 84 // åºæ¬ ã©ã¸ãª 85 const ans = document.createElement("div"); 86 ans.innerHTML = `<label>åºæ¬ </label>` + 87 radio("answer", "yes", "⯠åå ", true) + 88 radio("answer", "maybe", "â³ æªå®", false) + 89 radio("answer", "no", "à ä¸åå ", false); 90 card.appendChild(ans); 91 92 // è»åºã 93 const drive = document.createElement("div"); 94 const dl = document.createElement("label"); 95 const dc = document.createElement("input"); 96 dc.type = "checkbox"; 97 dc.id = "can-drive"; 98 dl.appendChild(dc);
99 dl.appendChild(document.createTextNode(" ð è»åºãå¯è½")); 100 drive.appendChild(dl); 101 card.appendChild(drive); 102 103 const submit = document.createElement("button"); 104 submit.className = "btn btn-primary"; 105 submit.textContent = "åçãéä¿¡ãã"; 106 submit.addEventListener("click", onSubmit); 107 card.appendChild(submit); 108 109 const inline = document.createElement("p"); 110 inline.className = "notice hidden"; 111 card.appendChild(inline); 112 container.appendChild(card); 113 114 // â :checked ã»ã¬ã¯ã¿ã¯ä½¿ããªãï¼ãã¹ãç°å¢ã® deno-dom ãä¸è´ããªãï¼ã.checked ã§è¦ã¦ã 115 // å¤ã¯å±æ§ããèªãï¼HTMLæååç±æ¥ã®è¦ç´ 㯠.value ã屿§ãåæ ããªãå®è£ ãããï¼ã 116 function selectedAnswer() { 117 const checked = [...container.querySelectorAll('input[name="answer"]')].find((r) => r.checked); 118 return checked ? checked.getAttribute("value") : "yes"; 119 } 120 121 async function onSubmit() { 122 const name = nameInput.value.trim(); 123 if (!name) { 124 inline.textContent = "ãååãå ¥åãã¦ãã ãã"; 125 inline.classList.remove("hidden"); 126 return; 127 } 128 submit.disabled = true; 129 deps.ui.showLoading(); 130 try { 131 const req = { 132 event_id: ev.id, 133 display_name: name, 134 answer: selectedAnswer(), 135 can_drive: dc.checked, 136 }; 137 if (id.success && id.id_token) req.id_token = id.id_token; 138 const editToken = deps.storage.getEditToken(ev.id); 139 if (editToken) req.edit_token = editToken; 140 const res = await deps.api.upsertAttendance(req); 141 deps.ui.hideLoading(); 142 if (res.edit_token_raw) deps.storage.saveEditToken(ev.id, res.edit_token_raw); 143 // èªåã®åçãããã¾ã®åçãã«åæ ãããæ¬äººã¯ player_id ã§åå®ããåã®è¡ã¯ç½®ãæããã 144 // æ¢å player ã®è¡¨ç¤ºåã¯ãµã¼ããæ¸ãæããªãã®ã§ãåã®è¡ãããã°ãã®ååã®ã¾ã¾åºãã 145 if (res.player_id) { 146 const prev = atts.find((a) => a.player && a.player.id === res.player_id); 147 atts = atts.filter((a) => a !== prev); 148 atts.push({ 149 id: res.attendance_id, 150 player: { id: res.player_id, display_name: prev ? prev.player.display_name : name }, 151 answer: req.answer, 152 can_drive: req.can_drive, 153 }); 154 } 155 renderDone(); 156 } catch (e) { 157 deps.ui.hideLoading(); 158 submit.disabled = false; 159 inline.textContent = e.message || "éä¿¡ã«å¤±æãã¾ãã"; 160 inline.classList.remove("hidden"); 161 } 162 } 163 } 164 165 function renderDone() { 166 container.innerHTML = ""; 167 const card = document.createElement("div"); 168 card.className = "card"; 169 card.innerHTML = `<div class="subhead">â åçãéä¿¡ãã¾ããï¼</div>`; 170 card.appendChild(roster()); 171 const change = document.createElement("button"); 172 change.className = "btn btn-sub"; 173 change.textContent = "åçã夿´ãã"; 174 change.addEventListener("click", () => renderForm()); 175 card.appendChild(change); 176 const close = document.createElement("button"); 177 close.className = "btn btn-primary"; 178 close.textContent = "â éãã"; 179 close.addEventListener("click", () => { 180 const ok = deps.ui.closeOrHome(deps, props.navigate, props.groupToken); 181 if (!ok) deps.ui.showToast("åçã¯éä¿¡æ¸ã¿ã§ãããã®ç»é¢ã¯éãã¦OKã§ã"); 182 }); 183 card.appendChild(close); 184 container.appendChild(card); 185 } 186 187 renderForm(); 188} 189 190function radio(name, value, label, checked) { 191 return `<label><input type="radio" name="${name}" value="${value}"${ 192 checked ? " checked" : "" 193 }/> ${label}</label>`; 194} 195function escapeHtml(s) { 196 return String(s).replace( 197 /[&<>"]/g, 198 (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c]), 199 ); 200}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.