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 package main
import ( import (
"fmt"
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
"streifling.com/jason/sicherheitsunterweisung/packages/data" "streifling.com/jason/sicherheitsunterweisung/packages/data"
"streifling.com/jason/sicherheitsunterweisung/packages/server" "streifling.com/jason/sicherheitsunterweisung/packages/server"
"streifling.com/jason/sicherheitsunterweisung/packages/types"
) )
func addUUIDs(uc *chan string, uuids *[]string) { func waitForParticipants(sb []*types.Briefing, sp []*types.Participant, cp <-chan *types.Participant, m *http.ServeMux) {
for uuid := range *uc { for p := range cp {
(*uuids) = append(*uuids, uuid) 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() { func main() {
var i, j int64 logins := make([]string, 0)
uuids := make([]string, 0) participants := make([]*types.Participant, 0)
i, j = 1, 1 briefings := make([]*types.Briefing, 0)
mux := http.NewServeMux() mux := http.NewServeMux()
db, err := data.OpenDB("sicherheitsunterweisung") db, err := data.OpenDB("sicherheitsunterweisung")
@ -25,21 +35,26 @@ func main() {
log.Fatalln(err) log.Fatalln(err)
} }
uuidChan := make(chan string) var i, j int64
defer close(uuidChan) if err := db.GetLastID(&i); err != nil {
go addUUIDs(&uuidChan, &uuids) 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.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static/"))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
template.Must(template.ParseFiles("templates/index.html", "templates/login.html")).Execute(w, nil) 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("/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("/submit-form/", server.SubmitForm(db, &i, &j))
mux.HandleFunc("/generate-uuid/", server.GenerateUUID(uuidChan))
mux.HandleFunc("/internal-login/", server.DisplayTable(db)) 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)) log.Fatalln(http.ListenAndServe(":8080", mux))
} }

View File

@ -19,27 +19,50 @@ type DB struct {
Name string Name string
} }
func getCredentials() (string, string, error) { func getUsername() (string, error) {
user := os.Getenv("DB_USER")
if user == "" {
var err error
fmt.Printf("DB Benutzer: ") fmt.Printf("DB Benutzer: ")
user, err := bufio.NewReader(os.Stdin).ReadString('\n') user, err = bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil { if err != nil {
return "", "", fmt.Errorf("getCredentials: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err) return "", fmt.Errorf("getUsername: bufio.NewReader(os.Stdin).ReadString('\n'): %v", err)
}
}
return strings.TrimSpace(user), nil
} }
func getPassword() (string, error) {
pass := os.Getenv("DB_PASS")
if pass == "" {
fmt.Printf("DB Passwort: ") fmt.Printf("DB Passwort: ")
bytePass, err := term.ReadPassword(int(syscall.Stdin)) bytePass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil { if err != nil {
return "", "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err) return "", fmt.Errorf("getCredentials: term.ReadPassword(int(syscall.Stdin)): %v", err)
} }
fmt.Println() fmt.Println()
pass := string(bytePass) pass = strings.TrimSpace(string(bytePass))
}
return strings.TrimSpace(user), strings.TrimSpace(pass), nil return pass, nil
} }
func reverseOrder(bs *[]types.Briefing) { func getCredentials() (string, string, error) {
for i, j := 0, len(*bs)-1; i < j; i, j = i+1, j-1 { user, err := getUsername()
(*bs)[i], (*bs)[j] = (*bs)[j], (*bs)[i] 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 return nil
} }
func (db *DB) ReadAll() (*[]types.Briefing, error) { func (db *DB) ReadAll() ([]*types.Briefing, error) {
bs := make([]types.Briefing, 0) bs := make([]*types.Briefing, 0)
rows, err := db.Query("SELECT *" + " FROM " + db.Name) rows, err := db.Query("SELECT *" + " FROM " + db.Name)
if err != nil { 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) return nil, fmt.Errorf("*DB.ReadAll: db.Query(): %v\n", err)
} }
b.Participants = append(b.Participants, *p) b.Participants = append(b.Participants, p)
bs = append(bs, *b) bs = append(bs, b)
} }
reverseOrder(&bs) reverseOrder(bs)
return &bs, nil return bs, nil
} }
func (db *DB) ReadByName(name string) (*[]types.Briefing, error) { func (db *DB) ReadByName(name string) ([]*types.Briefing, error) {
bs := make([]types.Briefing, 0) bs := make([]*types.Briefing, 0)
rows, err := db.Query("SELECT *"+ rows, err := db.Query("SELECT *"+
" FROM "+db.Name+ " 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,"+ " &b.LastName, &b.Date, &b.Time, &b.State, &b.Location, &p.FirstName,"+
" &p.LastName, &p.Company): %v\n", err) " &p.LastName, &p.Company): %v\n", err)
} }
b.Participants = append(b.Participants, *p) b.Participants = append(b.Participants, p)
bs = append(bs, *b) bs = append(bs, b)
} }
reverseOrder(&bs) reverseOrder(bs)
return &bs, nil 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 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" "html/template"
"log" "log"
"net/http" "net/http"
"strconv"
"strings"
"streifling.com/jason/sicherheitsunterweisung/packages/data" "streifling.com/jason/sicherheitsunterweisung/packages/data"
"streifling.com/jason/sicherheitsunterweisung/packages/types" "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 { func DisplayTable(db *data.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.ReadAll() bs, err := db.ReadAll()
if err != nil { if err != nil {
_ = fmt.Errorf("DisplayTable: %v\n", err) _ = 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) { return func(w http.ResponseWriter, r *http.Request) {
bs, err := db.ReadByName(r.PostFormValue("search")) bs, err := db.ReadByName(r.PostFormValue("search"))
if err != nil { if err != nil {
@ -33,14 +43,27 @@ func DisplayResults(db *data.DB) http.HandlerFunc {
func DisplayForm(i *int64) http.HandlerFunc { func DisplayForm(i *int64) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { 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) { return func(w http.ResponseWriter, r *http.Request) {
*i++ *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") b.Location = r.PostFormValue("location")
for ; *j <= *i; *j++ { for ; *j <= *i; *j++ {
b.Participants = append(b.Participants, types.Participant{ b.Participants = append(b.Participants, &types.Participant{
ID: *j, ID: *j,
Person: types.Person{ Person: types.Person{
FirstName: r.PostFormValue("participant-first-" + fmt.Sprint(*j)), 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 { // TODO: Make it only serve one purpose
return func(w http.ResponseWriter, r *http.Request) { func loginIsCorrect(l string, logins *[]string) bool {
bs := make([]byte, 4) for i, v := range *logins {
if l == v {
(*logins) = append((*logins)[:i], (*logins)[i+1:]...)
return true
}
}
return false
}
_, err := rand.Read(bs) func newParticipant(l string) (*types.Participant, error) {
p := new(types.Participant)
idInt, err := strconv.Atoi(strings.Split(l, "-")[0])
if err != nil { if err != nil {
_ = fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err) return nil, fmt.Errorf("newParticipant: strconv.Atoi(idString): %v\n", err)
} }
uuid := hex.EncodeToString(bs) p.ID = int64(idInt)
ch <- uuid return p, nil
template.Must(template.ParseFiles("templates/form.html")).ExecuteTemplate(w, "uuid", uuid)
}
} }
func DisplayQuestionsIfOK(uuids *[]string) http.HandlerFunc { func DisplayParticipantForm(ls *[]string, cp chan<- *types.Participant) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if uuidIsOK(r.PostFormValue("login"), uuids) { l := r.PostFormValue("login")
template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", nil)
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 { } else {
template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil) template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil)
} }
} }
} }
// TODO: Delete uuid from uuids func readAnswer(r *http.Request, p *types.Participant, i int) error {
func uuidIsOK(uuid string, uuids *[]string) bool { v, err := strconv.Atoi(r.PostFormValue("answer"))
for _, u := range *uuids { if err != nil {
if uuid == u { return fmt.Errorf("readAnswer: strconv.Atoi(r.PostFormValue(\"answer\")): %v\n", err)
return true }
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)
} }
} }
return false
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)
}
} }

View File

@ -7,28 +7,23 @@ type Person struct {
type Instructor Person type Instructor Person
type QAElement struct { type Answer struct {
ID int ID int
Text string Text string
} }
type Answer QAElement
type Question struct { type Question struct {
QAElement Text string
Answers []Answer Answers []Answer
} Chosen int
Correct int
type Questionaire struct {
UUID string
Questions []Question
} }
type Participant struct { type Participant struct {
ID int64 ID int64
Person Person
Company string Company string
QuestionaireUUID string Questions []Question
} }
type Briefing struct { type Briefing struct {
@ -37,5 +32,5 @@ type Briefing struct {
Time string Time string
State string State string
Location 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" }} {{ define "content" }}
<form>
<h2>Login</h2> <h2>Login</h2>
<form>
<label for="login-input">Code</label> <label for="login-input">Code</label>
<input type="text" name="login" id="login-input" /> <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" }} {{ define "answers" }}
{{ range .Answers }} {{ range .Q.Answers }}
<div>
<input type="radio" name="answer" id="answer-{{ .ID }}" value="{{ .ID }}" />
<label for="answer-{{ .ID }}">{{ .Text }}</label> <label for="answer-{{ .ID }}">{{ .Text }}</label>
<input type="radio" name="answer-{{ .ID }}" id="answer-{{ .ID }}" /> </div>
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ define "content" }} {{ define "content" }}
<h2>Frage {{ .Question.ID }}</h2> <h2>Frage {{ .I }}</h2>
<p>{{ .Question.Text }}</p> <p>{{ .Q.Text }}</p>
<form>
{{ template "answers" . }} {{ template "answers" . }}
<button type="submit" hx-post="/submit-{{ .UUID }}-{{ .Question.ID }}/" hx-target="#content" hx-swap="innerHTML"> <button type="submit" hx-post="/display-question-{{ .ID }}-{{ .J }}/" hx-target="#content" hx-swap="innerHTML">
Weiter
</button>
</form>
{{ end }} {{ 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.