-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathform.js
More file actions
137 lines (121 loc) · 4.62 KB
/
Copy pathform.js
File metadata and controls
137 lines (121 loc) · 4.62 KB
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
// Loads config.json and wires up form submission.
//
// Submission modes (resolved in priority order):
// 1. DIRECT — window.GITHUB_TOKEN is set (e.g. via a gitignored config.local.js).
// Posts directly to the GitHub Issues API from the browser.
// Acceptable when the token is intentionally scoped to issues:write on one repo
// and the submission content is non-sensitive. Suitable for GitHub Pages deploys.
//
// 2. SERVER — posts to /submit. The server holds the token (Netlify function,
// Cloudflare Pages function, or the k8s Node.js server). Recommended default.
(async function () {
// Load app config
let config = {};
try {
const res = await fetch("/config.json");
config = await res.json();
} catch {
console.warn("Could not load config.json — using defaults");
}
// Populate page text
document.getElementById("title").textContent = config.title || "Feedback";
setHTML("subtitle", config.subtitle || "");
setHTML("intro", config.intro || "");
setHTML("feedback-hint", config.feedback_hint || "");
setHTML("question-hint", config.question_hint || "");
if (config.title) document.title = config.title;
// Wire form
document.getElementById("feedback-form").addEventListener("submit", async (e) => {
e.preventDefault();
const form = e.target;
// Honeypot check
if (form.bot_trap && form.bot_trap.value) return;
const feedback = form.feedback.value.trim();
const question = form.question.value.trim();
if (!feedback && !question) {
showStatus("error", "Please fill in at least one field.");
return;
}
const btn = document.getElementById("submit-btn");
btn.disabled = true;
btn.textContent = "Sending…";
clearStatus();
const payload = {
feedback,
question,
email: form.email.value.trim(),
submitted_at: new Date().toISOString(),
};
try {
if (window.GITHUB_TOKEN) {
await submitDirect(payload, config);
} else {
await submitServer(payload);
}
showStatus("success", "Thanks — your response was recorded.");
form.reset();
} catch (err) {
showStatus("error", "Something went wrong. Please try again.");
console.error(err);
} finally {
btn.disabled = false;
btn.textContent = "Send feedback";
}
});
// ── submission paths ────────────────────────────────────────────────────────
async function submitServer(payload) {
const res = await fetch("/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(`Server error: ${res.status}`);
}
async function submitDirect(payload, config) {
const repo = window.GITHUB_REPO || config.github_repo;
if (!repo) throw new Error("GITHUB_REPO not configured");
const labels = config.labels || ["feedback"];
const body = formatIssueBody(payload);
const res = await fetch(`https://api.github.com/repos/${repo}/issues`, {
method: "POST",
headers: {
Authorization: `Bearer ${window.GITHUB_TOKEN}`,
"Content-Type": "application/json",
Accept: "application/vnd.github+json",
},
body: JSON.stringify({
title: `Feedback${payload.email ? " from " + payload.email : ""}`,
body,
labels,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.message || `GitHub API error: ${res.status}`);
}
}
// ── helpers ─────────────────────────────────────────────────────────────────
function formatIssueBody(payload) {
const lines = [];
if (payload.feedback) lines.push(`## Feedback\n\n${payload.feedback}`);
if (payload.question) lines.push(`## Data question\n\n${payload.question}`);
if (payload.email) lines.push(`**Contact:** ${payload.email}`);
lines.push(`**Submitted:** ${payload.submitted_at}`);
return lines.join("\n\n");
}
function setHTML(id, html) {
const el = document.getElementById(id);
if (el) el.innerHTML = html;
}
function showStatus(type, msg) {
const el = document.getElementById("status");
el.className = type;
el.textContent = msg;
}
function clearStatus() {
const el = document.getElementById("status");
el.className = "";
el.textContent = "";
el.style.display = "none";
}
})();