diff --git a/fff/04_dashboad/script_dashboad.js b/fff/04_dashboad/script_dashboad.js index 2133311..57da784 100644 --- a/fff/04_dashboad/script_dashboad.js +++ b/fff/04_dashboad/script_dashboad.js @@ -47,10 +47,10 @@ this.top3El = top3; this.barsEl = bars; this.headingEl = heading; - this.defaultHeading = heading?.textContent; // 最初の「漁港A / 漁港B」を保存 + this.defaultHeading = heading?.textContent; this.titleInput?.addEventListener("input", () => this.updateTitle()); - this.fileInput.addEventListener("change", (e) => this.onFile(e)); + this.fileInput?.addEventListener("change", (e) => this.onFile(e)); } log(line) { @@ -70,72 +70,85 @@ this.headingEl.textContent = t || this.defaultHeading || ""; } - async onFile(e) { + processCsvText(sourceName, text) { this.clear(); - try { - const file = e.target.files?.[0]; - if (!file) { this.log("ファイルが選択されていないよ"); return; } + this.log(`読み込み: ${sourceName}, ${text.length} bytes`); - // CSV ファイルを選んだときに、ファイル名を見出し&タイトルに出す - const baseName = file.name.replace(/\.csv$/i, ""); + const rows = parseCSV(text); + if (rows.length < 2) { this.log("CSVの行が不足"); return; } + + const headers = rows[0].map(h => h.trim()); + const { fishCol, amtCol } = detectColumns(headers); + this.log(`検出ヘッダー: fishCol=${fishCol}, amtCol=${amtCol}`); + if (fishCol === -1 || amtCol === -1) { + this.log("魚名または金額の列がない"); + return; + } + + const totals = new Map(); + for (let i = 1; i < rows.length; i++) { + const r = rows[i]; + const fish = (r[fishCol] ?? "").trim(); + if (!fish) continue; + const amt = toNumber(r[amtCol]); + totals.set(fish, (totals.get(fish) ?? 0) + amt); + } + + const sorted = Array.from(totals.entries()).sort((a,b) => b[1] - a[1]); + if (!sorted.length) { this.log("データがありません"); return; } + + const top3 = sorted.slice(0, 3); + top3.forEach(([name, val], i) => { + const li = document.createElement("li"); + li.className = `rank-${i+1}`; + li.innerHTML = `${i+1}位 + ${name} + ${val.toLocaleString()} 千円`; + this.top3El.appendChild(li); + }); + + const others = sorted.slice(3); + this.barsEl.innerHTML = ""; + if (others.length) { + const maxVal = Math.max(...others.map(([,v]) => v)); + others.forEach(([name, val]) => { + const row = document.createElement("div"); + row.className = "bar"; + row.innerHTML = `
4位以下はありません。
"; + } + + this.log("完了"); + } + + async loadFromUrl(url, labelText) { + try { + this.clear(); + this.log(`URLから読み込み中: ${url}`); + + const res = await fetch(url); + if (!res.ok) throw new Error(`HTTPエラー: ${res.status}`); + + const text = await res.text(); + + + const baseName = + (labelText && labelText.trim()) || + url.split("/").pop().replace(/\.csv$/i, "") || + url; + if (this.titleInput) this.titleInput.value = baseName; if (this.headingEl) this.headingEl.textContent = baseName; - const text = await file.text(); - this.log(`読み込み: ${file.name}, ${text.length} bytes`); - const rows = parseCSV(text); - if (rows.length < 2) { this.log("CSVの行が不足"); return; } - - const headers = rows[0].map(h => h.trim()); - const { fishCol, amtCol } = detectColumns(headers); - this.log(`検出ヘッダー: fishCol=${fishCol}, amtCol=${amtCol}`); - if (fishCol === -1 || amtCol === -1) { - this.log("魚名または金額の列がない"); - return; - } - - const totals = new Map(); - for (let i = 1; i < rows.length; i++) { - const r = rows[i]; - const fish = (r[fishCol] ?? "").trim(); - if (!fish) continue; - const amt = toNumber(r[amtCol]); - totals.set(fish, (totals.get(fish) ?? 0) + amt); - } - - const sorted = Array.from(totals.entries()).sort((a,b) => b[1] - a[1]); - if (!sorted.length) { this.log("データがありません"); return; } - - const top3 = sorted.slice(0, 3); - top3.forEach(([name, val], i) => { - const li = document.createElement("li"); - li.className = `rank-${i+1}`; - li.innerHTML = `${i+1}位 - ${name} - ${val.toLocaleString()} 千円`; - this.top3El.appendChild(li); - }); - - const others = sorted.slice(3); - this.barsEl.innerHTML = ""; - if (others.length) { - const maxVal = Math.max(...others.map(([,v]) => v)); - others.forEach(([name, val]) => { - const row = document.createElement("div"); - row.className = "bar"; - row.innerHTML = `4位以下はありません。
"; - } - - this.log("完了"); + this.processCsvText(baseName, text); } catch (err) { console.error(err); this.log("エラー: " + err.message); @@ -147,7 +160,7 @@ window.addEventListener("DOMContentLoaded", () => { const panelA = new Panel({ fileInput: document.getElementById("csvA"), - titleInput: document.getElementById("titleA"), + titleInput: document.getElementById("titleA"), // なければ null でOK log: document.getElementById("msgA"), top3: document.getElementById("top3A"), bars: document.getElementById("barsA"), @@ -156,39 +169,32 @@ const panelB = new Panel({ fileInput: document.getElementById("csvB"), - titleInput: document.getElementById("titleB"), + titleInput: document.getElementById("titleB"), // なければ null でOK log: document.getElementById("msgB"), top3: document.getElementById("top3B"), bars: document.getElementById("barsB"), heading: document.querySelector("#panelB h2"), }); -}); -window.addEventListener("DOMContentLoaded", () => { - const csvA = document.getElementById("csvA"); - const csvB = document.getElementById("csvB"); - const h2A = document.querySelector("#panelA h2"); - const h2B = document.querySelector("#panelB h2"); - - if (csvA && h2A) { - csvA.addEventListener("change", () => { - const file = csvA.files[0]; - if (file) { - const name = file.name.replace(/\.csv$/i, ""); - h2A.textContent = name; - } + //漁港A選択 + const selectA = document.getElementById("csvASelect"); + if (selectA) { + selectA.addEventListener("change", () => { + const url = selectA.value; + if (!url) return; + const label = selectA.options[selectA.selectedIndex].textContent; + panelA.loadFromUrl(url, label); }); } - if (csvB && h2B) { - csvB.addEventListener("change", () => { - const file = csvB.files[0]; - if (file) { - const name = file.name.replace(/\.csv$/i, ""); - h2B.textContent = name; - } + // 漁港B選択 + const selectB = document.getElementById("csvBSelect"); + if (selectB) { + selectB.addEventListener("change", () => { + const url = selectB.value; + if (!url) return; + const label = selectB.options[selectB.selectedIndex].textContent; + panelB.loadFromUrl(url, label); }); } }); - -