Großer Umbruch der Architektur, halb fertig, zu viele Details zum Aufzählen, in Zukunft wieder kleine Commits!

This commit is contained in:
Jason Streifling 2023-10-16 18:51:52 +02:00
parent 726c8b6dcb
commit 76f1fe9588
16 changed files with 483 additions and 158 deletions

44
.air.toml Normal file
View File

@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 0
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

1
.gitignore vendored
View File

@ -1 +0,0 @@
create_tables.sql

96
create_tables.sql Normal file
View File

@ -0,0 +1,96 @@
USE sicherheitsunterweisung;
DROP TABLE IF EXISTS instructors;
DROP TABLE IF EXISTS briefings;
DROP TABLE IF EXISTS participants;
DROP TABLE IF EXISTS questions;
DROP TABLE IF EXISTS given_answers;
CREATE TABLE instructors (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
personnel_id INT NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE briefings (
id INT NOT NULL AUTO_INCREMENT,
date DATE NOT NULL,
time TIME NOT NULL,
location VARCHAR(32) NOT NULL,
as_of DATE NOT NULL,
instructor_id INT NOT NULL,
PRIMARY KEY(id),
FOREIGN KEY(instructor_id) REFERENCES instructors(id)
);
CREATE TABLE participants (
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(32) NOT NULL,
last_name VARCHAR(32) NOT NULL,
company VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE questions (
id INT NOT NULL AUTO_INCREMENT,
question VARCHAR(256) NOT NULL,
answer_1 VARCHAR(64) NOT NULL,
answer_2 VARCHAR(64) NOT NULL,
answer_3 VARCHAR(64) NOT NULL,
answer_4 VARCHAR(64) NOT NULL,
correct_answer INT NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE given_answers (
briefing_id INT NOT NULL,
participant_id INT NOT NULL,
question_id INT NOT NULL,
given_answer INT NOT NULL,
PRIMARY KEY(briefing_id, participant_id, question_id),
FOREIGN KEY(briefing_id) REFERENCES briefings(id),
FOREIGN KEY(participant_id) REFERENCES participants(id),
FOREIGN KEY(question_id) REFERENCES questions(id)
);
INSERT INTO instructors
(first_name, last_name, personnel_id)
VALUES
('Jason', 'Streifling', '300484'),
('Tim', 'Matzuga', '300483'),
('Georg', 'Bränzel', '300485');
INSERT INTO briefings (
date, time, location, as_of, instructor_id
) VALUES
( '2023-10-16', '17:00:00', 'Werk Langenhagen', '2021-02-01', '1' ),
( '2023-10-16', '17:05:00', 'Werk Langenhagen', '2021-02-01', '2' );
INSERT INTO participants (
first_name, last_name, company
) VALUES
( 'Jan', 'Schiele', 'Körber' ),
( 'Murat', 'Zorlu', 'MP Technic' );
INSERT INTO questions (
question, answer_1, answer_2, answer_3, answer_4, correct_answer
) VALUES
( 'Was ist 1+1?', '1', '2', '3', '4', '2' ),
( 'Was ist 1+2?', '1', '2', '3', '4', '3' ),
( 'Was ist 2+2?', '1', '2', '3', '4', '4' );
INSERT INTO given_answers (
briefing_id, participant_id, question_id, given_answer
) VALUES
( '1', '1', '1', '2' ),
( '1', '1', '2', '3' ),
( '1', '1', '3', '3' ),
( '2', '2', '1', '2' ),
( '2', '2', '2', '3' ),
( '2', '2', '3', '4' );

41
main.go
View File

@ -1,23 +1,33 @@
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"streifling.com/jason/sicherheitsunterweisung/packages/data"
"streifling.com/jason/sicherheitsunterweisung/packages/server"
"streifling.com/jason/sicherheitsunterweisung/packages/types"
)
func addUUIDs(uc *chan string, uuids *[]string) {
for uuid := range *uc {
(*uuids) = append(*uuids, uuid)
func waitForParticipants(sb []*types.Briefing, sp []*types.Participant, cp <-chan *types.Participant, m *http.ServeMux) {
for p := range cp {
p.Questions = data.InitQuestions()
sp = append(sp, p)
var i int
for i = range p.Questions {
m.HandleFunc("/display-question-"+fmt.Sprintf("%d", p.ID)+"-"+fmt.Sprintf("%d", i)+"/", server.DisplayQuestion(i, p))
}
m.HandleFunc("/display-question-"+fmt.Sprintf("%d", p.ID)+"-"+fmt.Sprintf("%d", i+1)+"/", server.DisplayTestResults(sb, p))
}
}
func main() {
var i, j int64
uuids := make([]string, 0)
i, j = 1, 1
logins := make([]string, 0)
participants := make([]*types.Participant, 0)
briefings := make([]*types.Briefing, 0)
mux := http.NewServeMux()
db, err := data.OpenDB("sicherheitsunterweisung")
@ -25,21 +35,26 @@ func main() {
log.Fatalln(err)
}
uuidChan := make(chan string)
defer close(uuidChan)
go addUUIDs(&uuidChan, &uuids)
var i, j int64
if err := db.GetLastID(&i); err != nil {
log.Fatalln(err)
}
j = i
participantChan := make(chan *types.Participant)
defer close(participantChan)
go waitForParticipants(briefings, participants, participantChan, mux)
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
template.Must(template.ParseFiles("templates/index.html", "templates/login.html")).Execute(w, nil)
})
mux.HandleFunc("/search/", server.DisplayResults(db))
mux.HandleFunc("/search/", server.DisplaySearchResults(db))
mux.HandleFunc("/new-briefing/", server.DisplayForm(&i))
mux.HandleFunc("/add-participant/", server.AddParticipant(&i))
mux.HandleFunc("/add-participant/", server.AddParticipant(&i, &logins))
mux.HandleFunc("/submit-form/", server.SubmitForm(db, &i, &j))
mux.HandleFunc("/generate-uuid/", server.GenerateUUID(uuidChan))
mux.HandleFunc("/internal-login/", server.DisplayTable(db))
mux.HandleFunc("/external-login/", server.DisplayQuestionsIfOK(&uuids))
mux.HandleFunc("/external-login/", server.DisplayParticipantForm(&logins, participantChan))
log.Fatalln(http.ListenAndServe(":8080", mux))
}

View File

@ -19,27 +19,50 @@ type DB struct {
Name string
}
func getCredentials() (string, string, error) {
fmt.Printf("DB Benutzer: ")
user, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", "", fmt.Errorf("getCredentials: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err)
func getUsername() (string, error) {
user := os.Getenv("DB_USER")
if user == "" {
var err error
fmt.Printf("DB Benutzer: ")
user, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", fmt.Errorf("getUsername: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err)
}
}
fmt.Printf("DB Passwort: ")
bytePass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err)
}
fmt.Println()
pass := string(bytePass)
return strings.TrimSpace(user), strings.TrimSpace(pass), nil
return strings.TrimSpace(user), nil
}
func reverseOrder(bs *[]types.Briefing) {
for i, j := 0, len(*bs)-1; i < j; i, j = i+1, j-1 {
(*bs)[i], (*bs)[j] = (*bs)[j], (*bs)[i]
func getPassword() (string, error) {
pass := os.Getenv("DB_PASS")
if pass == "" {
fmt.Printf("DB Passwort: ")
bytePass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
return "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err)
}
fmt.Println()
pass = strings.TrimSpace(string(bytePass))
}
return pass, nil
}
func getCredentials() (string, string, error) {
user, err := getUsername()
if err != nil {
return "", "", fmt.Errorf("getCredentials: getUsername(): %v", err)
}
pass, err := getPassword()
if err != nil {
return "", "", fmt.Errorf("getCredentials: getPassword(): %v", err)
}
return user, pass, nil
}
func reverseOrder(bs []*types.Briefing) {
for i, j := 0, len(bs)-1; i < j; i, j = i+1, j-1 {
bs[i], bs[j] = bs[j], bs[i]
}
}
@ -92,8 +115,8 @@ func (db *DB) WriteBriefing(b *types.Briefing) error {
return nil
}
func (db *DB) ReadAll() (*[]types.Briefing, error) {
bs := make([]types.Briefing, 0)
func (db *DB) ReadAll() ([]*types.Briefing, error) {
bs := make([]*types.Briefing, 0)
rows, err := db.Query("SELECT *" + " FROM " + db.Name)
if err != nil {
@ -110,16 +133,16 @@ func (db *DB) ReadAll() (*[]types.Briefing, error) {
return nil, fmt.Errorf("*DB.ReadAll: db.Query(): %v\n", err)
}
b.Participants = append(b.Participants, *p)
bs = append(bs, *b)
b.Participants = append(b.Participants, p)
bs = append(bs, b)
}
reverseOrder(&bs)
return &bs, nil
reverseOrder(bs)
return bs, nil
}
func (db *DB) ReadByName(name string) (*[]types.Briefing, error) {
bs := make([]types.Briefing, 0)
func (db *DB) ReadByName(name string) ([]*types.Briefing, error) {
bs := make([]*types.Briefing, 0)
rows, err := db.Query("SELECT *"+
" FROM "+db.Name+
@ -148,10 +171,22 @@ func (db *DB) ReadByName(name string) (*[]types.Briefing, error) {
" &b.LastName, &b.Date, &b.Time, &b.State, &b.Location, &p.FirstName,"+
" &p.LastName, &p.Company): %v\n", err)
}
b.Participants = append(b.Participants, *p)
bs = append(bs, *b)
b.Participants = append(b.Participants, p)
bs = append(bs, b)
}
reverseOrder(&bs)
return &bs, nil
reverseOrder(bs)
return bs, nil
}
func (db *DB) GetLastID(i *int64) error {
row := db.QueryRow("SELECT id" +
" FROM " + db.Name +
" ORDER BY id DESC LIMIT 0, 1")
if err := row.Scan(i); err != nil {
return fmt.Errorf("*DB.GetLastID: row.Scan(&i): %v\n", err)
}
return nil
}

View File

@ -1 +1,42 @@
package data
import "streifling.com/jason/sicherheitsunterweisung/packages/types"
func InitQuestions() []types.Question {
Q := make([]types.Question, 0)
Q = append(Q, types.Question{
Text: "Wie viel ist 1+1?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 1,
})
Q = append(Q, types.Question{
Text: "Wie viel ist 2+2?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 3,
})
Q = append(Q, types.Question{
Text: "Wie viel ist 1+2?",
Answers: []types.Answer{
{ID: 0, Text: "1"},
{ID: 1, Text: "2"},
{ID: 2, Text: "3"},
{ID: 3, Text: "4"},
},
Correct: 2,
})
return Q
}

View File

@ -7,21 +7,31 @@ import (
"html/template"
"log"
"net/http"
"strconv"
"strings"
"streifling.com/jason/sicherheitsunterweisung/packages/data"
"streifling.com/jason/sicherheitsunterweisung/packages/types"
)
type questionData struct {
ID int64
Q types.Question
I int
J int
}
func DisplayTable(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.ReadAll()
if err != nil {
_ = fmt.Errorf("DisplayTable: %v\n", err)
}
template.Must(template.ParseFiles("templates/index.html", "templates/table.html")).Execute(w, bs)
template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "content", bs)
}
}
func DisplayResults(db *data.DB) http.HandlerFunc {
func DisplaySearchResults(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.ReadByName(r.PostFormValue("search"))
if err != nil {
@ -33,14 +43,27 @@ func DisplayResults(db *data.DB) http.HandlerFunc {
func DisplayForm(i *int64) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "content", i)
template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "content", i)
}
}
func AddParticipant(i *int64) http.HandlerFunc {
func generateUUID() string {
bs := make([]byte, 2)
if _, err := rand.Read(bs); err != nil {
_ = fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err)
return ""
}
return hex.EncodeToString(bs)
}
func AddParticipant(i *int64, ls *[]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
*i++
template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "participant", i)
login := fmt.Sprintf("%d", *i) + "-" + generateUUID()
(*ls) = append(*ls, login)
template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "new", login)
}
}
@ -56,7 +79,7 @@ func SubmitForm(db *data.DB, i, j *int64) http.HandlerFunc {
b.Location = r.PostFormValue("location")
for ; *j <= *i; *j++ {
b.Participants = append(b.Participants, types.Participant{
b.Participants = append(b.Participants, &types.Participant{
ID: *j,
Person: types.Person{
FirstName: r.PostFormValue("participant-first-" + fmt.Sprint(*j)),
@ -77,38 +100,107 @@ func SubmitForm(db *data.DB, i, j *int64) http.HandlerFunc {
}
}
func GenerateUUID(ch chan<- string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bs := make([]byte, 4)
_, err := rand.Read(bs)
if err != nil {
_ = fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err)
// TODO: Make it only serve one purpose
func loginIsCorrect(l string, logins *[]string) bool {
for i, v := range *logins {
if l == v {
(*logins) = append((*logins)[:i], (*logins)[i+1:]...)
return true
}
uuid := hex.EncodeToString(bs)
ch <- uuid
template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "uuid", uuid)
}
return false
}
func DisplayQuestionsIfOK(uuids *[]string) http.HandlerFunc {
func newParticipant(l string) (*types.Participant, error) {
p := new(types.Participant)
idInt, err := strconv.Atoi(strings.Split(l, "-")[0])
if err != nil {
return nil, fmt.Errorf("newParticipant: strconv.Atoi(idString): %v\n", err)
}
p.ID = int64(idInt)
return p, nil
}
func DisplayParticipantForm(ls *[]string, cp chan<- *types.Participant) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if uuidIsOK(r.PostFormValue("login"), uuids) {
template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", nil)
l := r.PostFormValue("login")
if loginIsCorrect(l, ls) {
p, err := newParticipant(l)
if err != nil {
http.Error(w, "GetParticipantData: newParticipant(l): "+fmt.Sprint(err), http.StatusInternalServerError)
}
cp <- p
template.Must(template.ParseFiles("templates/participant.html")).ExecuteTemplate(w, "content", p.ID)
} else {
template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
}
}
}
// TODO: Delete uuid from uuids
func uuidIsOK(uuid string, uuids *[]string) bool {
for _, u := range *uuids {
if uuid == u {
return true
}
func readAnswer(r *http.Request, p *types.Participant, i int) error {
v, err := strconv.Atoi(r.PostFormValue("answer"))
if err != nil {
return fmt.Errorf("readAnswer: strconv.Atoi(r.PostFormValue(\"answer\")): %v\n", err)
}
p.Questions[i].Chosen = v
return nil
}
func DisplayQuestion(i int, p *types.Participant) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if i == 0 {
p.FirstName = r.PostFormValue("participant-first-" + fmt.Sprintf("%d", p.ID))
p.LastName = r.PostFormValue("participant-last-" + fmt.Sprintf("%d", p.ID))
p.Company = r.PostFormValue("participant-company-" + fmt.Sprintf("%d", p.ID))
} else {
if err := readAnswer(r, p, i-1); err != nil {
http.Error(w, "DisplayQuestion: readAnswer(r, p, i): "+fmt.Sprint(err), http.StatusInternalServerError)
}
}
data := new(questionData)
data.ID = p.ID
data.Q = p.Questions[i]
data.I = i
data.J = i + 1
template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
}
}
func DisplayTestResults(b *types.Briefing, p *types.Participant) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
numQuestions := len(p.Questions)
wrongAnswers := make([]int, 0)
fmt.Println(wrongAnswers)
if err := readAnswer(r, p, numQuestions-1); err != nil {
http.Error(w, "DisplayTestResults: readAnswer(r, p, i): "+fmt.Sprint(err), http.StatusInternalServerError)
}
for i, q := range p.Questions {
if q.Chosen != q.Correct {
wrongAnswers = append(wrongAnswers, i)
}
}
if wrongAnswers == nil {
b.Participants = append(b.Participants, p)
} else {
data := new(questionData)
data.ID = p.ID
data.Q = p.Questions[0]
data.I = 0
data.J = data.I + 1
template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data)
}
template.Must(template.ParseFiles("templates/results.html")).ExecuteTemplate(w, "content", nil)
}
return false
}

View File

@ -7,28 +7,23 @@ type Person struct {
type Instructor Person
type QAElement struct {
type Answer struct {
ID int
Text string
}
type Answer QAElement
type Question struct {
QAElement
Text string
Answers []Answer
}
type Questionaire struct {
UUID string
Questions []Question
Chosen int
Correct int
}
type Participant struct {
ID int64
Person
Company string
QuestionaireUUID string
Company string
Questions []Question
}
type Briefing struct {
@ -37,5 +32,5 @@ type Briefing struct {
Time string
State string
Location string
Participants []Participant
Participants []*Participant
}

38
templates/briefing.html Normal file
View File

@ -0,0 +1,38 @@
{{ define "add-button" }}
<button type="button" hx-post="/add-participant/" hx-target="this" hx-swap="outerHTML">
Neuer Teilnehmer
</button>
{{ end }}
{{ define "new" }}
<span>{{ . }}</span>
{{ template "add-button" . }}
{{ end }}
{{ define "content" }}
<form>
<div id="instructor">
<label for="instructor-first-input">Unterweiser Vorname</label>
<input type="text" name="instructor-first" id="instructor-first-input" />
<label for="instructor-last-input">Unterweiser Nachname</label>
<input type="text" name="instructor-last" id="instructor-last-input" />
</div>
<div id="location">
<label for="location-input">Ort</label>
<input type="text" name="location" id="location-input" />
</div>
<div id="state">
<label for="state-input">Stand vom</label>
<input type="date" name="state" id="state-input" />
</div>
{{ template "add-button" . }}
<button type="submit" hx-post="/submit-form/" hx-target="#content" hx-swap="innerHTML">
OK
</button>
</form>
{{ end }}

View File

@ -1,64 +0,0 @@
{{ define "uuid" }}
<span>{{ . }}</span>
{{ end }}
{{ define "participant" }}
<div id="participant-{{ . }}">
<label for="participant-first-input-{{ . }}">Vorname</label>
<input type="text" name="participant-first-{{ . }}" id="participant-first-input-{{ . }}" />
<label for="participant-last-input-{{ . }}">Nachname</label>
<input type="text" name="participant-last-{{ . }}" id="participant-last-input-{{ . }}" />
<label for="participant-company-input-{{ . }}">Firma</label>
<input type="text" name="participant-company-{{ . }}" id="participant-company-input-{{ . }}" />
<button type="button" hx-post="/generate-uuid/" hx-target="this" hx-swap="outerHTML">
Einladen
</button>
</div>
{{ end }}
{{ define "content" }}
<form>
<div id="instructor">
<label for="instructor-first-input">Unterweiser Vorname</label>
<input type="text" name="instructor-first" id="instructor-first-input" />
<label for="instructor-last-input">Unterweiser Nachname</label>
<input type="text" name="instructor-last" id="instructor-last-input" />
</div>
<div id="date">
<label for="date-input">Datum</label>
<input type="date" name="date" id="date-input" />
</div>
<div id="time">
<label for="time-input">Uhrzeit</label>
<input type="time" name="time" id="time-input" />
</div>
<div id="state">
<label for="state-input">Stand vom</label>
<input type="date" name="state" id="state-input" />
</div>
<div id="location">
<label for="location-input">Ort</label>
<input type="text" name="location" id="location-input" />
</div>
<div id="participants">
<button type="button" hx-post="/add-participant/" hx-target="#participants" hx-swap="beforeend" hx-trigger="click">
Neuer Teilnehmer
</button>
{{ template "participant" . }}
</div>
<button type="submit" hx-post="/submit-form/" hx-target="#content" hx-swap="innerHTML">
Senden
</button>
</form>
{{ end }}

View File

@ -1,7 +1,7 @@
{{ define "content" }}
<form>
<h2>Login</h2>
<h2>Login</h2>
<form>
<label for="login-input">Code</label>
<input type="text" name="login" id="login-input" />

View File

@ -0,0 +1,16 @@
{{ define "content" }}
<form id="participant-{{ . }}">
<label for="participant-first-input-{{ . }}">Vorname</label>
<input type="text" name="participant-first-{{ . }}" id="participant-first-input-{{ . }}" />
<label for="participant-last-input-{{ . }}">Nachname</label>
<input type="text" name="participant-last-{{ . }}" id="participant-last-input-{{ . }}" />
<label for="participant-company-input-{{ . }}">Firma</label>
<input type="text" name="participant-company-{{ . }}" id="participant-company-input-{{ . }}" />
<button type="button" hx-post="/display-question-{{ . }}-0/" hx-target="#content">
Fertig
</button>
</form>
{{ end }}

View File

@ -1,15 +1,21 @@
{{ define "answers" }}
{{ range .Answers }}
<label for="answer-{{ .ID }}">{{ .Text }}</label>
<input type="radio" name="answer-{{ .ID }}" id="answer-{{ .ID }}" />
{{ range .Q.Answers }}
<div>
<input type="radio" name="answer" id="answer-{{ .ID }}" value="{{ .ID }}" />
<label for="answer-{{ .ID }}">{{ .Text }}</label>
</div>
{{ end }}
{{ end }}
{{ define "content" }}
<h2>Frage {{ .Question.ID }}</h2>
<p>{{ .Question.Text }}</p>
<h2>Frage {{ .I }}</h2>
<p>{{ .Q.Text }}</p>
{{ template "answers" . }}
<form>
{{ template "answers" . }}
<button type="submit" hx-post="/submit-{{ .UUID }}-{{ .Question.ID }}/" hx-target="#content" hx-swap="innerHTML">
{{ end }}
<button type="submit" hx-post="/display-question-{{ .ID }}-{{ .J }}/" hx-target="#content" hx-swap="innerHTML">
Weiter
</button>
</form>
{{ end }}

11
templates/results.html Normal file
View File

@ -0,0 +1,11 @@
{{ define "passed" }}
{{ end }}
{{ define "failed" }}
{{ end }}
{{ define "content" }}
{{ q := range .Participant.Questions }}
<p>{{ . }}{{ q.Text }}</p>
{{ end }}
{{ end }}

1
tmp/build-errors.log Normal file
View File

@ -0,0 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
tmp/main Executable file

Binary file not shown.