Initial Commit
This commit is contained in:
parent
a6219c1f64
commit
3df7fb525c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Remove database
|
||||||
|
formdata.db
|
35
admin.html
Normal file
35
admin.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin - Submissions</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Submitted Answers</h1>
|
||||||
|
<button id="dark-mode-toggle">Toggle Dark Mode</button>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Question</th>
|
||||||
|
<th>Answer</th>
|
||||||
|
</tr>
|
||||||
|
{{range .}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.QuestionText}}</td>
|
||||||
|
<td>{{.Answer}}</td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No submissions found.</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Link the dark mode toggle script -->
|
||||||
|
<script src="/static/theme.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module ergosthing
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require github.com/mattn/go-sqlite3 v1.14.22
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -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=
|
25
index.html
Normal file
25
index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Form</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Submit Your Information</h1>
|
||||||
|
<button id="dark-mode-toggle">Toggle Dark Mode</button>
|
||||||
|
<form action="/submit" method="post">
|
||||||
|
{{range .}}
|
||||||
|
<label>{{.QuestionText}}:</label>
|
||||||
|
<input type="{{.QuestionType}}" name="custom_{{.ID}}" required><br><br>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/theme.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
235
main.go
Normal file
235
main.go
Normal file
@ -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))
|
||||||
|
}
|
44
manage.html
Normal file
44
manage.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Questions</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Manage Questions</h1>
|
||||||
|
|
||||||
|
<!-- Dark mode toggle button -->
|
||||||
|
<button id="dark-mode-toggle">Toggle Dark Mode</button>
|
||||||
|
|
||||||
|
<h2>Add New Question</h2>
|
||||||
|
<form action="/manage/add" method="post">
|
||||||
|
<label for="question_text">Question Text:</label>
|
||||||
|
<input type="text" id="question_text" name="question_text" required><br><br>
|
||||||
|
<input type="submit" value="Add Question">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Existing Questions</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Question Text</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
{{range .}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.QuestionText}}</td>
|
||||||
|
<td><a href="/manage/remove?id={{.ID}}">Remove</a></td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No questions found.</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/theme.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
502
output.txt
Normal file
502
output.txt
Normal file
@ -0,0 +1,502 @@
|
|||||||
|
|
||||||
|
--- O:\Web Development\ErgosThing\admin.html ---
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin - Submissions</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Submitted Answers</h1>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Question</th>
|
||||||
|
<th>Answer</th>
|
||||||
|
</tr>
|
||||||
|
{{range .}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.QuestionText}}</td>
|
||||||
|
<td>{{.Answer}}</td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No submissions found.</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
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 ---
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Form</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Submit Your Information</h1>
|
||||||
|
<form action="/submit" method="post">
|
||||||
|
{{range .}}
|
||||||
|
<label>{{.QuestionText}}:</label>
|
||||||
|
<input type="{{.QuestionType}}" name="custom_{{.ID}}" required><br><br>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
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 ---
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manage Questions</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Manage Questions</h1>
|
||||||
|
<h2>Add New Question</h2>
|
||||||
|
<form action="/manage/add" method="post">
|
||||||
|
<label for="question_text">Question Text:</label>
|
||||||
|
<input type="text" id="question_text" name="question_text" required><br><br>
|
||||||
|
<input type="submit" value="Add Question">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Existing Questions</h2>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Question Text</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
{{range .}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.QuestionText}}</td>
|
||||||
|
<td><a href="/manage/remove?id={{.ID}}">Remove</a></td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No questions found.</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
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
|
147
static/style.css
Normal file
147
static/style.css
Normal file
@ -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;
|
||||||
|
}
|
23
static/theme.js
Normal file
23
static/theme.js
Normal file
@ -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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user