diff --git a/tools/buglist/app.orig.js b/tools/buglist/app.orig.js
new file mode 100644
index 000000000..e4453c524
--- /dev/null
+++ b/tools/buglist/app.orig.js
@@ -0,0 +1,83 @@
+// A prototype of a custom issues list/dashboard.
+// Copy this file (app.orig.js) to app.js for safety,
+// and in app.js, set token to a github personal access token
+// from https://github.com/settings/personal-access-tokens/new .
+// Then open index.html in a browser.
+
+const token = '';
+
+
+const getIssues = (name) => {
+ // https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues
+ // up to 100 most open bugs, most recently created first XXX with a severityN label
+ fetch(`https://api.github.com/repos/${name}/issues?per_page=100&labels=A-BUG`, //&label=severity1,severity2,severity3,severity4,severity5`,
+ {
+ headers: {
+ 'Accept': 'application/vnd.github.v3+json',
+ 'Authorization': `token ${token}`
+ }
+ })
+ .then(response => response.json())
+ .then(data => {
+ // console.log(data)
+ showIssues(data);
+ })
+ .catch(error => console.error(error))
+}
+
+const showIssues = (data) => {
+ let t = document.getElementById('issues');
+ let r = t.insertRow();
+ r.innerHTML = `
User pain
`;
+
+ for (let i in data) {
+ let r = t.insertRow();
+ let issue = data[i];
+
+ c = r.insertCell();
+ c.style = `white-space:nowrap; font-weight:bold;`;
+ let imp = issueImpact(issue);
+ let sev = issueSeverity(issue);
+ let maxscore = 25; // max impact * max severity
+ let pain = (sev && imp) ? sev * imp / maxscore : '';
+ c.innerHTML = pain ? `${pain}` : '';
+
+ c = r.insertCell();
+ c.innerHTML = `#${issue.number} ${issue.title}`;
+
+ c = r.insertCell();
+ c.innerHTML = '';
+
+ let sortedlabels = issue.labels.toSorted( (a,b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()) );
+ for (let j in sortedlabels) { c.innerHTML += showLabel(sortedlabels[j]); }
+
+ // c = r.insertCell();
+ // c.innerHTML = `${iss.user.login}`;
+ }
+}
+
+const issueFindLabelWithPrefix = (issue, prefix) => {
+ for (let i in issue.labels) {
+ let l = issue.labels[i];
+ if (l.name.startsWith(prefix)) {return l.name;}
+ }
+ return '';
+}
+
+
+const issueSeverity = (issue) => {
+ let l = issueFindLabelWithPrefix(issue, 'severity');
+ return l ? l.replace('severity','') : '';
+}
+
+const issueImpact = (issue) => {
+ let l = issueFindLabelWithPrefix(issue, 'impact');
+ return l ? l.replace('impact','') : '';
+}
+
+const showLabel = (label) => {
+ cls = label.name.startsWith('severity') || label.name.startsWith('impact') ? 'paletag' : 'tag';
+ return ` ` + label.name + '';
+}
+
+getIssues('simonmichael/hledger');
diff --git a/tools/buglist/index.html b/tools/buglist/index.html
new file mode 100644
index 000000000..9588fbfd8
--- /dev/null
+++ b/tools/buglist/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+ hledger bugs dashboard
+
+
+
+
+
hledger open bugs
+
+ Currently our user pain score is
+ Impact (number of people affected, 1-5) * Severity (for those affected, 1-5) / 25.
+
+ The possible scores are:
+0.04
+0.08
+0.12
+0.16
+0.20
+0.24
+0.32
+0.36
+0.40
+0.48
+0.60
+0.64
+0.80
+1.00
+.
+