diff --git a/index.html b/index.html
index 4672d2b..ba62ebf 100644
--- a/index.html
+++ b/index.html
@@ -23,6 +23,7 @@
Projects
Training
Contact
+ Logbook
GitHub
diff --git a/logbook.html b/logbook.html
new file mode 100644
index 0000000..ea909b6
--- /dev/null
+++ b/logbook.html
@@ -0,0 +1,87 @@
+
+
+
+
+
Andrew's Logbook
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Logbook
+
+ If you'd like, you can leave a message in my logbook so that I and others might see it.
+
+
+ Note that I do check all logs and some basic information about who sent them (IP address, browser, etc). Inappropriate comments will be removed and their authors banned.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/logbook.js b/scripts/logbook.js
new file mode 100644
index 0000000..6e9c2f3
--- /dev/null
+++ b/scripts/logbook.js
@@ -0,0 +1,84 @@
+const LOGBOOK_URL = "http://localhost:8080";
+
+const messagesContainer = document.getElementById("messages-container");
+const form = document.getElementById("logbook-form");
+const formMessageBox = document.getElementById("logbook-form-message-box");
+form.onsubmit = async (e) => {
+ e.preventDefault();
+ const data = {
+ name: document.getElementById("logbook-name-input").value,
+ message: document.getElementById("logbook-message-input").value
+ };
+ formMessageBox.style.display = "block";
+ formMessageBox.innerText = "Sending log entry...";
+ try {
+ const response = await fetch(LOGBOOK_URL, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json"
+ },
+ body: JSON.stringify(data)
+ });
+ if (response.ok) {
+ formMessageBox.innerText = "Log entry added!";
+ form.style.display = "none"; // Hide the form.
+ renderMessages(await fetchMessages());
+ } else {
+ formMessageBox.innerText = "Log entry rejected: " + response.status;
+ }
+ } catch (error) {
+ formMessageBox.innerText = "An error occurred: " + error;
+ }
+}
+
+// On startup, load the latest messages and show them.
+const messagesPromise = fetchMessages();
+messagesPromise.then(messages => renderMessages(messages));
+
+
+
+async function fetchMessages() {
+ const response = await fetch(LOGBOOK_URL);
+ if (response.ok) {
+ return await response.json();
+ } else {
+ console.error("Couldn't get log messages.", response.status);
+ return [];
+ }
+}
+
+function renderMessages(messages) {
+ messagesContainer.innerHTML = "";
+ messages.forEach(message => {
+ const messageElement = document.createElement("div");
+ messageElement.className = "logbook-message";
+
+ const timestampElement = document.createElement("time");
+ const date = Date(message.createdAt);
+ timestampElement.dateTime = message.createdAt;
+ timestampElement.innerText = date.toLocaleString();
+
+ const authorElement = document.createElement("strong");
+ authorElement.innerText = message.name;
+ const saidElement = document.createElement("span");
+ saidElement.innerText = " said: " + message.message;
+
+ const messageBodyElement = document.createElement("p");
+ messageBodyElement.appendChild(authorElement);
+ messageBodyElement.appendChild(saidElement);
+
+ messageElement.appendChild(timestampElement);
+ messageElement.appendChild(messageBodyElement);
+ messagesContainer.appendChild(messageElement);
+ });
+}
+
+function escapeHtml(unsafe) {
+ return unsafe
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
diff --git a/styles/logbook.css b/styles/logbook.css
new file mode 100644
index 0000000..d9844ef
--- /dev/null
+++ b/styles/logbook.css
@@ -0,0 +1,13 @@
+#messages-container {
+ margin-top: 1em;
+}
+
+.logbook-message {
+ background-color: var(--background-color-2);
+ margin: 0.5em 0;
+ padding: 0.5em;
+}
+
+.logbook-message time {
+ display: block;
+}