diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c537ea5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+# Remove database
+formdata.db
diff --git a/admin.html b/admin.html
new file mode 100644
index 0000000..32d16c7
--- /dev/null
+++ b/admin.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Admin - Submissions
+
+
+
+
+
Submitted Answers
+
+
+
+
+ Question |
+ Answer |
+
+ {{range .}}
+
+ {{.QuestionText}} |
+ {{.Answer}} |
+
+ {{else}}
+
+ No submissions found. |
+
+ {{end}}
+
+
+
+
+
+
+
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..8d63baa
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module ergosthing
+
+go 1.23.0
+
+require github.com/mattn/go-sqlite3 v1.14.22
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..e8d092a
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9729640
--- /dev/null
+++ b/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+ Form
+
+
+
+
+
Submit Your Information
+
+
+
+
+
+
+
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..eeb1c76
--- /dev/null
+++ b/main.go
@@ -0,0 +1,235 @@
+package main
+
+import (
+ "database/sql"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ _ "github.com/mattn/go-sqlite3"
+)
+
+
+type Question struct {
+ ID int
+ QuestionText string
+ QuestionType string
+}
+
+var db *sql.DB
+
+func renderForm(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query("SELECT question_text, question_type FROM questions ORDER BY question_order")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch form questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questions []Question
+
+ for rows.Next() {
+ var question Question
+ err := rows.Scan(&question.QuestionText, &question.QuestionType)
+ if err != nil {
+ log.Printf("Error scanning question: %v\n", err)
+ continue
+ }
+ questions = append(questions, question)
+ }
+
+ tmpl := template.Must(template.ParseFiles("index.html"))
+ tmpl.Execute(w, questions)
+}
+
+func handleFormSubmit(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ r.ParseForm()
+
+ rows, err := db.Query("SELECT id FROM questions")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questionID int
+ for rows.Next() {
+ err := rows.Scan(&questionID)
+ if err != nil {
+ log.Printf("Error scanning question ID: %v\n", err)
+ continue
+ }
+ fieldName := fmt.Sprintf("custom_%d", questionID)
+ fieldValue := r.FormValue(fieldName)
+
+ if fieldValue != "" {
+ insertQuery := `INSERT INTO answers (question_id, answer) VALUES (?, ?)`
+ _, err = db.Exec(insertQuery, questionID, fieldValue)
+ if err != nil {
+ log.Printf("Error saving answer: %v\n", err)
+ http.Error(w, "Failed to save answer", http.StatusInternalServerError)
+ return
+ }
+ } else {
+ log.Printf("No answer found for question %d", questionID)
+ }
+ }
+
+ fmt.Fprintf(w, "Thank you for your submission!")
+}
+
+
+func handleAdmin(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query(`
+ SELECT q.question_text, a.answer
+ FROM answers a
+ JOIN questions q ON a.question_id = q.id
+ ORDER BY q.question_order
+ `)
+ if err != nil {
+ log.Printf("Error fetching submissions: %v\n", err)
+ http.Error(w, "Failed to fetch submissions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var submissions []struct {
+ QuestionText string
+ Answer string
+ }
+
+ for rows.Next() {
+ var submission struct {
+ QuestionText string
+ Answer string
+ }
+ err := rows.Scan(&submission.QuestionText, &submission.Answer)
+ if err != nil {
+ log.Printf("Error scanning submission: %v\n", err)
+ continue
+ }
+ submissions = append(submissions, submission)
+ }
+
+ tmpl := template.Must(template.ParseFiles("admin.html"))
+ tmpl.Execute(w, submissions)
+}
+
+func handleAddQuestion(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ r.ParseForm()
+ questionText := r.FormValue("question_text")
+
+ insertQuery := `INSERT INTO questions (question_text, question_type, question_order) VALUES (?, 'text', (SELECT IFNULL(MAX(question_order), 0) + 1 FROM questions));`
+ _, err := db.Exec(insertQuery, questionText)
+ if err != nil {
+ log.Printf("Error adding new question: %v\n", err)
+ http.Error(w, "Failed to add question", http.StatusInternalServerError)
+ return
+ }
+
+ http.Redirect(w, r, "/manage", http.StatusSeeOther)
+}
+
+
+func handleManage(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query("SELECT id, question_text, question_type FROM questions ORDER BY question_order")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questions []Question
+
+ for rows.Next() {
+ var question Question
+ err := rows.Scan(&question.ID, &question.QuestionText, &question.QuestionType)
+ if err != nil {
+ log.Printf("Error scanning question: %v\n", err)
+ continue
+ }
+ questions = append(questions, question)
+ }
+
+ if len(questions) == 0 {
+ log.Println("No questions found")
+ }
+
+ tmpl := template.Must(template.ParseFiles("manage.html"))
+ err = tmpl.Execute(w, questions)
+ if err != nil {
+ log.Printf("Error executing template: %v\n", err)
+ }
+}
+
+func handleRemoveQuestion(w http.ResponseWriter, r *http.Request) {
+ questionID := r.URL.Query().Get("id")
+
+ deleteQuery := `DELETE FROM questions WHERE id = ?`
+ _, err := db.Exec(deleteQuery, questionID)
+ if err != nil {
+ log.Printf("Error removing question: %v\n", err)
+ http.Error(w, "Failed to remove question", http.StatusInternalServerError)
+ return
+ }
+
+ http.Redirect(w, r, "/manage", http.StatusSeeOther)
+}
+
+
+func main() {
+ var err error
+
+ db, err = sql.Open("sqlite3", "./formdata.db")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ createTableQueries := `
+ CREATE TABLE IF NOT EXISTS questions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ question_text TEXT NOT NULL,
+ question_type TEXT NOT NULL DEFAULT 'text',
+ question_order INTEGER NOT NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS answers (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ question_id INTEGER NOT NULL,
+ answer TEXT NOT NULL,
+ FOREIGN KEY (question_id) REFERENCES questions(id)
+ );
+ `
+
+ _, err = db.Exec(createTableQueries)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
+
+ http.HandleFunc("/", renderForm)
+ http.HandleFunc("/submit", handleFormSubmit)
+ http.HandleFunc("/admin", handleAdmin)
+ http.HandleFunc("/manage", handleManage)
+ http.HandleFunc("/manage/add", handleAddQuestion)
+ http.HandleFunc("/manage/remove", handleRemoveQuestion)
+
+ fmt.Println("Server started at :8080")
+
+ log.Fatal(http.ListenAndServe(":8080", nil))
+}
diff --git a/manage.html b/manage.html
new file mode 100644
index 0000000..8ed607e
--- /dev/null
+++ b/manage.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+ Manage Questions
+
+
+
+
+
Manage Questions
+
+
+
+
+
Add New Question
+
+
+
Existing Questions
+
+
+ Question Text |
+ Actions |
+
+ {{range .}}
+
+ {{.QuestionText}} |
+ Remove |
+
+ {{else}}
+
+ No questions found. |
+
+ {{end}}
+
+
+
+
+
+
diff --git a/output.txt b/output.txt
new file mode 100644
index 0000000..3873e61
--- /dev/null
+++ b/output.txt
@@ -0,0 +1,502 @@
+
+--- O:\Web Development\ErgosThing\admin.html ---
+
+
+
+
+
+ Admin - Submissions
+
+
+
+
+
Submitted Answers
+
+
+ Question |
+ Answer |
+
+ {{range .}}
+
+ {{.QuestionText}} |
+ {{.Answer}} |
+
+ {{else}}
+
+ No submissions found. |
+
+ {{end}}
+
+
+
+
+
+END --- O:\Web Development\ErgosThing\admin.html
+
+--- O:\Web Development\ErgosThing\go.mod ---
+module ergosthing
+
+go 1.23.0
+
+require github.com/mattn/go-sqlite3 v1.14.22
+
+END --- O:\Web Development\ErgosThing\go.mod
+
+--- O:\Web Development\ErgosThing\go.sum ---
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+
+END --- O:\Web Development\ErgosThing\go.sum
+
+--- O:\Web Development\ErgosThing\index.html ---
+
+
+
+
+
+ Form
+
+
+
+
+
Submit Your Information
+
+
+
+
+
+END --- O:\Web Development\ErgosThing\index.html
+
+--- O:\Web Development\ErgosThing\main.go ---
+package main
+
+import (
+ "database/sql"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ _ "github.com/mattn/go-sqlite3"
+)
+
+
+type Question struct {
+ ID int
+ QuestionText string
+ QuestionType string
+}
+
+var db *sql.DB
+
+func renderForm(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query("SELECT question_text, question_type FROM questions ORDER BY question_order")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch form questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questions []Question
+
+ for rows.Next() {
+ var question Question
+ err := rows.Scan(&question.QuestionText, &question.QuestionType)
+ if err != nil {
+ log.Printf("Error scanning question: %v\n", err)
+ continue
+ }
+ questions = append(questions, question)
+ }
+
+ tmpl := template.Must(template.ParseFiles("index.html"))
+ tmpl.Execute(w, questions)
+}
+
+func handleFormSubmit(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ r.ParseForm()
+
+ rows, err := db.Query("SELECT id FROM questions")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questionID int
+ for rows.Next() {
+ err := rows.Scan(&questionID)
+ if err != nil {
+ log.Printf("Error scanning question ID: %v\n", err)
+ continue
+ }
+ fieldName := fmt.Sprintf("custom_%d", questionID)
+ fieldValue := r.FormValue(fieldName)
+
+ if fieldValue != "" {
+ insertQuery := `INSERT INTO answers (question_id, answer) VALUES (?, ?)`
+ _, err = db.Exec(insertQuery, questionID, fieldValue)
+ if err != nil {
+ log.Printf("Error saving answer: %v\n", err)
+ http.Error(w, "Failed to save answer", http.StatusInternalServerError)
+ return
+ }
+ } else {
+ log.Printf("No answer found for question %d", questionID)
+ }
+ }
+
+ fmt.Fprintf(w, "Thank you for your submission!")
+}
+
+
+func handleAdmin(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query(`
+ SELECT q.question_text, a.answer
+ FROM answers a
+ JOIN questions q ON a.question_id = q.id
+ ORDER BY q.question_order
+ `)
+ if err != nil {
+ log.Printf("Error fetching submissions: %v\n", err)
+ http.Error(w, "Failed to fetch submissions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var submissions []struct {
+ QuestionText string
+ Answer string
+ }
+
+ for rows.Next() {
+ var submission struct {
+ QuestionText string
+ Answer string
+ }
+ err := rows.Scan(&submission.QuestionText, &submission.Answer)
+ if err != nil {
+ log.Printf("Error scanning submission: %v\n", err)
+ continue
+ }
+ submissions = append(submissions, submission)
+ }
+
+ tmpl := template.Must(template.ParseFiles("admin.html"))
+ tmpl.Execute(w, submissions)
+}
+
+func handleAddQuestion(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
+ return
+ }
+
+ r.ParseForm()
+ questionText := r.FormValue("question_text")
+
+ insertQuery := `INSERT INTO questions (question_text, question_type, question_order) VALUES (?, 'text', (SELECT IFNULL(MAX(question_order), 0) + 1 FROM questions));`
+ _, err := db.Exec(insertQuery, questionText)
+ if err != nil {
+ log.Printf("Error adding new question: %v\n", err)
+ http.Error(w, "Failed to add question", http.StatusInternalServerError)
+ return
+ }
+
+ http.Redirect(w, r, "/manage", http.StatusSeeOther)
+}
+
+
+func handleManage(w http.ResponseWriter, r *http.Request) {
+ rows, err := db.Query("SELECT id, question_text, question_type FROM questions ORDER BY question_order")
+ if err != nil {
+ log.Printf("Error fetching questions: %v\n", err)
+ http.Error(w, "Failed to fetch questions", http.StatusInternalServerError)
+ return
+ }
+ defer rows.Close()
+
+ var questions []Question
+
+ for rows.Next() {
+ var question Question
+ err := rows.Scan(&question.ID, &question.QuestionText, &question.QuestionType)
+ if err != nil {
+ log.Printf("Error scanning question: %v\n", err)
+ continue
+ }
+ questions = append(questions, question)
+ }
+
+ if len(questions) == 0 {
+ log.Println("No questions found")
+ }
+
+ tmpl := template.Must(template.ParseFiles("manage.html"))
+ err = tmpl.Execute(w, questions)
+ if err != nil {
+ log.Printf("Error executing template: %v\n", err)
+ }
+}
+
+func handleRemoveQuestion(w http.ResponseWriter, r *http.Request) {
+ questionID := r.URL.Query().Get("id")
+
+ deleteQuery := `DELETE FROM questions WHERE id = ?`
+ _, err := db.Exec(deleteQuery, questionID)
+ if err != nil {
+ log.Printf("Error removing question: %v\n", err)
+ http.Error(w, "Failed to remove question", http.StatusInternalServerError)
+ return
+ }
+
+ http.Redirect(w, r, "/manage", http.StatusSeeOther)
+}
+
+
+func main() {
+ var err error
+
+ db, err = sql.Open("sqlite3", "./formdata.db")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ createTableQueries := `
+ CREATE TABLE IF NOT EXISTS questions (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ question_text TEXT NOT NULL,
+ question_type TEXT NOT NULL DEFAULT 'text',
+ question_order INTEGER NOT NULL
+ );
+
+ CREATE TABLE IF NOT EXISTS answers (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ question_id INTEGER NOT NULL,
+ answer TEXT NOT NULL,
+ FOREIGN KEY (question_id) REFERENCES questions(id)
+ );
+ `
+
+ _, err = db.Exec(createTableQueries)
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+
+ http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
+
+ http.HandleFunc("/", renderForm)
+ http.HandleFunc("/submit", handleFormSubmit)
+ http.HandleFunc("/admin", handleAdmin)
+ http.HandleFunc("/manage", handleManage)
+ http.HandleFunc("/manage/add", handleAddQuestion)
+ http.HandleFunc("/manage/remove", handleRemoveQuestion)
+
+ fmt.Println("Server started at :8080")
+
+ log.Fatal(http.ListenAndServe(":8080", nil))
+}
+
+END --- O:\Web Development\ErgosThing\main.go
+
+--- O:\Web Development\ErgosThing\manage.html ---
+
+
+
+
+
+ Manage Questions
+
+
+
+
+
Manage Questions
+
Add New Question
+
+
+
Existing Questions
+
+
+ Question Text |
+ Actions |
+
+ {{range .}}
+
+ {{.QuestionText}} |
+ Remove |
+
+ {{else}}
+
+ No questions found. |
+
+ {{end}}
+
+
+
+
+
+END --- O:\Web Development\ErgosThing\manage.html
+
+--- O:\Web Development\ErgosThing\typer.py ---
+import os
+import sys
+from pathspec import PathSpec
+from pathspec.patterns import GitWildMatchPattern
+
+def load_gitignore_patterns(gitignore_file):
+ with open(gitignore_file, 'r') as f:
+ lines = f.readlines()
+ spec = PathSpec.from_lines(GitWildMatchPattern, lines)
+ return spec
+
+def print_file_content(file_path, output_file):
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
+ content = file.read()
+ output = f"\n--- {file_path} ---\n{content}\nEND --- {file_path}\n"
+ print(output)
+ with open(output_file, 'a', encoding='utf-8') as out_file:
+ out_file.write(output)
+
+def walk_and_print(directory, spec, gitignore_path, output_file, exclude_cmakelists):
+ # Overwrite the output file if it exists
+ if os.path.exists(output_file):
+ os.remove(output_file)
+
+ for root, dirs, files in os.walk(directory):
+ # Always ignore 'assets' and 'art' directories
+ dirs[:] = [d for d in dirs if d not in ('assets', 'art') and not spec.match_file(os.path.relpath(os.path.join(root, d), directory))]
+ files = [f for f in files if not spec.match_file(os.path.relpath(os.path.join(root, f), directory))]
+
+ if exclude_cmakelists:
+ files = [f for f in files if f != 'CMakeLists.txt']
+
+ for file_name in files:
+ file_path = os.path.join(root, file_name)
+ # Exclude the .gitignore file itself from being printed
+ if os.path.abspath(file_path) != os.path.abspath(gitignore_path):
+ print_file_content(file_path, output_file)
+
+if __name__ == "__main__":
+ current_directory = os.getcwd()
+ gitignore_path = os.path.join(current_directory, '.gitignore')
+ output_file_path = os.path.join(current_directory, 'output.txt')
+
+ exclude_cmakelists = '-nocmake' in sys.argv
+
+ if os.path.exists(gitignore_path):
+ spec = load_gitignore_patterns(gitignore_path)
+ else:
+ spec = PathSpec([]) # Empty spec if no .gitignore is found
+
+ walk_and_print(current_directory, spec, gitignore_path, output_file_path, exclude_cmakelists)
+
+END --- O:\Web Development\ErgosThing\typer.py
+
+--- O:\Web Development\ErgosThing\static\style.css ---
+body {
+ font-family: Arial, sans-serif;
+ background-color: #f4f4f9;
+ margin: 0;
+ padding: 0;
+}
+
+.container {
+ width: 80%;
+ margin: 0 auto;
+ padding: 20px;
+ background-color: white;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ margin-top: 50px;
+ border-radius: 10px;
+}
+
+h1 {
+ text-align: center;
+ color: #333;
+}
+
+form {
+ margin: 20px 0;
+}
+
+form label {
+ display: block;
+ margin-bottom: 10px;
+ font-weight: bold;
+ color: #555;
+}
+
+form input[type="text"], form input[type="email"], form textarea {
+ width: 100%;
+ padding: 10px;
+ margin-bottom: 20px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ font-size: 16px;
+}
+
+form input[type="submit"] {
+ background-color: #28a745;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+}
+
+form input[type="submit"]:hover {
+ background-color: #218838;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+
+table, th, td {
+ border: 1px solid #ddd;
+}
+
+th, td {
+ padding: 10px;
+ text-align: left;
+}
+
+th {
+ background-color: #f2f2f2;
+}
+
+td {
+ background-color: white;
+}
+
+a {
+ color: #007bff;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+END --- O:\Web Development\ErgosThing\static\style.css
diff --git a/static/style.css b/static/style.css
new file mode 100644
index 0000000..3bdf986
--- /dev/null
+++ b/static/style.css
@@ -0,0 +1,147 @@
+body {
+ font-family: Arial, sans-serif;
+ background-color: #f4f4f9;
+ color: #333;
+ margin: 0;
+ padding: 0;
+}
+
+.container {
+ width: 80%;
+ margin: 0 auto;
+ padding: 20px;
+ background-color: white;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ margin-top: 50px;
+ border-radius: 10px;
+}
+
+h1 {
+ text-align: center;
+ color: #333; /* Default color for light mode */
+}
+
+form {
+ margin: 20px 0;
+}
+
+form label {
+ display: block;
+ margin-bottom: 10px;
+ font-weight: bold;
+ color: #555;
+}
+
+form input[type="text"], form input[type="email"], form textarea {
+ width: 100%;
+ padding: 10px;
+ margin-bottom: 20px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ font-size: 16px;
+ background-color: white;
+ color: #333;
+}
+
+form input[type="submit"] {
+ background-color: #28a745;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+}
+
+form input[type="submit"]:hover {
+ background-color: #218838;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+
+table, th, td {
+ border: 1px solid #ddd;
+}
+
+th, td {
+ padding: 10px;
+ text-align: left;
+}
+
+th {
+ background-color: #f2f2f2;
+ color: #333;
+}
+
+td {
+ background-color: white;
+ color: #333;
+}
+
+a {
+ color: #007bff;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+/*
+ * DARK MODE
+ */
+body.dark-mode {
+ background-color: #1a1a1a;
+ color: #f4f4f9;
+}
+
+.container.dark-mode {
+ background-color: #333;
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.1);
+}
+
+h1.dark-mode {
+ color: #f4f4f9; /* Ensure that the heading is white and visible */
+}
+
+form label.dark-mode {
+ color: #f4f4f9;
+}
+
+form input[type="text"].dark-mode, form input[type="email"].dark-mode, form textarea.dark-mode {
+ background-color: #555;
+ color: #f4f4f9;
+ border: 1px solid #888;
+}
+
+form input[type="submit"].dark-mode {
+ background-color: #28a745;
+ color: white;
+}
+
+table.dark-mode {
+ background-color: #444;
+}
+
+th.dark-mode {
+ background-color: #555;
+ color: #f4f4f9;
+}
+
+td.dark-mode {
+ background-color: #666;
+ color: #f4f4f9;
+}
+
+a.dark-mode {
+ color: #66b3ff;
+}
+
+a.dark-mode:hover {
+ color: #3399ff;
+ text-decoration: underline;
+}
diff --git a/static/theme.js b/static/theme.js
new file mode 100644
index 0000000..0f13072
--- /dev/null
+++ b/static/theme.js
@@ -0,0 +1,23 @@
+document.addEventListener("DOMContentLoaded", function () {
+ const darkModeToggle = document.getElementById('dark-mode-toggle');
+
+ if (localStorage.getItem('theme') === 'dark') {
+ document.body.classList.add('dark-mode');
+ document.querySelectorAll('.container, label, th, td, a, table, h1').forEach(el => {
+ el.classList.add('dark-mode');
+ });
+ }
+
+ darkModeToggle.addEventListener('click', function () {
+ document.body.classList.toggle('dark-mode');
+ document.querySelectorAll('.container, label, th, td, a, table, h1').forEach(el => {
+ el.classList.toggle('dark-mode');
+ });
+
+ if (document.body.classList.contains('dark-mode')) {
+ localStorage.setItem('theme', 'dark');
+ } else {
+ localStorage.setItem('theme', 'light');
+ }
+ });
+});