diff --git a/create_tables.sql b/create_tables.sql index aa6df3b..f9bed8a 100644 --- a/create_tables.sql +++ b/create_tables.sql @@ -9,16 +9,15 @@ DROP TABLE IF EXISTS given_answers; SET FOREIGN_KEY_CHECKS = 1; 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, + id INT NOT NULL, + first_name VARCHAR(32) NOT NULL, + last_name VARCHAR(32) NOT NULL, PRIMARY KEY(id) ); CREATE TABLE briefings ( - id INT NOT NULL AUTO_INCREMENT, + id INT AUTO_INCREMENT, date DATE NOT NULL, time TIME NOT NULL, location VARCHAR(32) NOT NULL, @@ -31,7 +30,7 @@ CREATE TABLE briefings ( ); CREATE TABLE participants ( - id INT NOT NULL AUTO_INCREMENT, + id INT AUTO_INCREMENT, first_name VARCHAR(32) NOT NULL, last_name VARCHAR(32) NOT NULL, company VARCHAR(32) NOT NULL, @@ -40,7 +39,7 @@ CREATE TABLE participants ( ); CREATE TABLE questions ( - id INT NOT NULL AUTO_INCREMENT, + id INT AUTO_INCREMENT, question VARCHAR(256) NOT NULL, answer_1 VARCHAR(64) NOT NULL, answer_2 VARCHAR(64) NOT NULL, @@ -64,17 +63,17 @@ CREATE TABLE given_answers ( ); INSERT INTO instructors - (first_name, last_name, personnel_id) + (id, first_name, last_name) VALUES - ( 'Jason', 'Streifling', '123456' ), - ( 'Tim', 'Taler', '123457' ), - ( 'Georg', 'aus dem Jungel', '123458' ); + ( '123456', 'Jason', 'Streifling' ), + ( '123457', 'Tim', 'Taler' ), + ( '123458', 'Georg', 'aus dem Jungel' ); INSERT INTO briefings ( date, time, location, document_name, as_of, instructor_id ) VALUES - ( '2023-10-16', '17:00:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '1' ), - ( '2023-10-16', '17:05:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '2' ); + ( '2023-10-16', '17:00:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '123456' ), + ( '2023-10-16', '17:05:00', 'Werk Langenhagen', 'ICS-2021-LGH', '2021-02-01', '123457' ); INSERT INTO participants ( first_name, last_name, company diff --git a/main.go b/main.go index b4ea527..a09a0ca 100644 --- a/main.go +++ b/main.go @@ -11,11 +11,37 @@ import ( "streifling.com/jason/sicherheitsunterweisung/packages/types" ) -func handleSessions(mux *http.ServeMux, db *db.DB, ss []*types.Session, cs <-chan *types.Session) { +func handleParticipants(mux *http.ServeMux, db *db.DB, cp <-chan *types.Participant, s *types.Session) { + for participant := range cp { + mux.HandleFunc("/submit-participant/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(participant.Login)+"/", server.HandleParticipant(s, participant, &s.Questions, db)) + for i := range s.Questions { + mux.HandleFunc("/submit-answer/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(participant.Login)+"/"+fmt.Sprint(i+1)+"/", server.HandleAnswer(s, db, participant, &s.Questions, int64(i+1))) + } + mux.HandleFunc("/retry/"+fmt.Sprint(s.ID)+"/"+fmt.Sprint(participant.Login)+"/", server.HandleRetry(s, participant, &s.Questions)) + } +} + +func handleSessions(mux *http.ServeMux, db *db.DB, cs <-chan *types.Session, ss *[]*types.Session) { for session := range cs { - ss = append(ss, session) - mux.HandleFunc("/add-participant-"+fmt.Sprint(session.ID)+"/", server.AddParticipant(session)) - mux.HandleFunc("/submit-form-"+fmt.Sprint(session.ID)+"/", server.SubmitBriefingForm(session, db)) + (*ss) = append(*ss, session) + participantChan := make(chan *types.Participant) + questionIDs := make([]string, 4) + + for i := 0; i < len(questionIDs); i++ { + questionIDs[i] = fmt.Sprint(i + 1) + } + + var err error + session.Questions, err = db.GetQuestions(questionIDs) + if err != nil { + log.Fatalln(err) + } + + mux.HandleFunc("/new-briefing/", server.HandleNewBriefing(session)) + mux.HandleFunc("/new-participant/"+fmt.Sprint(session.ID)+"/", server.HandleNewParticipant(session, participantChan)) + mux.HandleFunc("/submit-form/"+fmt.Sprint(session.ID)+"/", server.HandleBriefingForm(session, db)) + + go handleParticipants(mux, db, participantChan, session) } } @@ -25,20 +51,19 @@ func main() { log.Fatalln(err) } + mux := http.NewServeMux() sessions := make([]*types.Session, 0) sessionChan := make(chan *types.Session) - mux := http.NewServeMux() 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("/internal-login/", server.DisplayTable(db)) - mux.HandleFunc("/external-login/", server.DisplayParticipantForm(sessions)) - mux.HandleFunc("/search/", server.DisplaySearchResults(db)) - mux.HandleFunc("/new-briefing/", server.DisplayInstructorForm(db, sessionChan)) + mux.HandleFunc("/internal-login/", server.HandleInternalLogin(&sessions, sessionChan, db)) + mux.HandleFunc("/external-login/", server.HandleExternalLogin(&sessions)) + mux.HandleFunc("/search/", server.HandleSearch(db)) - go handleSessions(mux, db, sessions, sessionChan) + go handleSessions(mux, db, sessionChan, &sessions) log.Fatalln(http.ListenAndServe(":8080", mux)) } diff --git a/packages/db/db.go b/packages/db/db.go index 6124258..67ef0c6 100644 --- a/packages/db/db.go +++ b/packages/db/db.go @@ -120,7 +120,7 @@ func (db *DB) WriteParticipant(p *types.Participant) error { return nil } -func (db *DB) WriteGivenAnswer(b *types.Briefing, p *types.Participant, q *types.Question, g *types.GivenAnswer) error { +func (db *DB) WriteGivenAnswer(b *types.Briefing, p *types.Participant, q *types.Question, g int) error { _, err := db.Exec(` INSERT INTO given_answers (briefing_id, participant_id, question_id, given_answer) @@ -147,7 +147,7 @@ func (db *DB) WriteAllDataOfBriefing(b *types.Briefing, sp *[]*types.Participant for _, p := range *sp { for i, q := range *sq { - db.WriteGivenAnswer(b, p, q, (*sg)[i]) + db.WriteGivenAnswer(b, p, q, i) } } @@ -302,11 +302,12 @@ func (db *DB) GetInstructors() ([]*types.Instructor, error) { if err != nil { return nil, fmt.Errorf("*DB.GetInstructors: db.Query(): %v\n", err) } + defer rows.Close() instructors := make([]*types.Instructor, 0) for rows.Next() { instructor := new(types.Instructor) - if err = rows.Scan(&instructor.ID, &instructor.FirstName, &instructor.LastName, &instructor.PersonnelID); err != nil { + if err = rows.Scan(&instructor.ID, &instructor.FirstName, &instructor.LastName); err != nil { return nil, fmt.Errorf("*DB.GetInstructors: rows.Scan(): %v\n", err) } instructors = append(instructors, instructor) @@ -314,3 +315,67 @@ func (db *DB) GetInstructors() ([]*types.Instructor, error) { return instructors, nil } + +func (db *DB) GetQuestions(nums []string) ([]types.Question, error) { + rows, err := db.Query(` + SELECT * + FROM questions + WHERE id IN (` + strings.Join(nums, ", ") + `) + `) + if err != nil { + return nil, fmt.Errorf("*DB.GetQuestions: db.Query(): %v\n", err) + } + defer rows.Close() + + questions := make([]types.Question, 0) + for rows.Next() { + q := new(types.Question) + a1 := new(types.Answer) + a2 := new(types.Answer) + a3 := new(types.Answer) + a4 := new(types.Answer) + + a1.ID = 1 + a2.ID = 2 + a3.ID = 3 + a4.ID = 4 + + if err := rows.Scan(&q.ID, &q.Text, &a1.Text, &a2.Text, &a3.Text, &a4.Text, &q.Correct); err != nil { + return nil, fmt.Errorf("*DB.GetQuestions: rows.Scan(): %v\n", err) + } + + q.Answers = append(q.Answers, *a1) + q.Answers = append(q.Answers, *a2) + q.Answers = append(q.Answers, *a3) + q.Answers = append(q.Answers, *a4) + + questions = append(questions, *q) + } + + return questions, nil +} + +func (db *DB) GetGivenAnswers(bid, pid int64, sq []types.Question) ([]int, error) { + answers := make([]int, 0) + query := ` + SELECT given_answer + FROM given_answers + WHERE + briefing_id = ? AND + participant_id = ? AND + question_id = ? + ` + + for _, q := range sq { + var answer int + + row := db.QueryRow(query, bid, pid, q.ID) + if err := row.Scan(&answer); err != nil { + return nil, fmt.Errorf("*DB.GetGivenAnswers: row.Scan(): %v\n", err) + } + + answers = append(answers, answer) + } + + return answers, nil +} diff --git a/packages/server/server.go b/packages/server/server.go index 0deff44..319cf7d 100644 --- a/packages/server/server.go +++ b/packages/server/server.go @@ -23,13 +23,32 @@ func displayTable(w http.ResponseWriter, db *db.DB) { template.Must(template.ParseFiles("templates/table.html")).ExecuteTemplate(w, "content", bs) } -func DisplayTable(db *db.DB) http.HandlerFunc { +func HandleInternalLogin(ss *[]*types.Session, cs chan<- *types.Session, db *db.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - displayTable(w, db) + instructors, err := db.GetInstructors() + if err != nil { + http.Error(w, "HandleInternalLogin: db.GetInstructors(): "+fmt.Sprint(err), http.StatusInternalServerError) + log.Panicln(err) + } + + for _, i := range instructors { + if r.PostFormValue("login") == fmt.Sprint(i.ID) { + session := new(types.Session) + session.ID = uuid.New() + session.Briefing = new(types.Briefing) + session.Briefing.InstructorID = i.ID + (*ss) = append((*ss), session) + cs <- session + + displayTable(w, db) + return + } + } + template.Must(template.ParseFiles("templates/login.html")).ExecuteTemplate(w, "content", nil) } } -func DisplaySearchResults(db *db.DB) http.HandlerFunc { +func HandleSearch(db *db.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { bs, err := db.GetOverviewTableDataByName(r.PostFormValue("search")) if err != nil { @@ -39,52 +58,30 @@ func DisplaySearchResults(db *db.DB) http.HandlerFunc { } } -func DisplayInstructorForm(db *db.DB, cs chan<- *types.Session) http.HandlerFunc { +func HandleNewBriefing(s *types.Session) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - type option struct { - ID int64 - String string - } - - type htmlData struct { + type httpData struct { SessionID uuid.UUID - Options []option } - session := new(types.Session) - session.ID = uuid.New() - cs <- session - - instructors, err := db.GetInstructors() - if err != nil { - http.Error(w, "DisplayInstructorForm: db.GetInstructors(): "+fmt.Sprint(err), http.StatusInternalServerError) - log.Panicln(err) - } - - data := new(htmlData) - data.SessionID = session.ID - for _, instructor := range instructors { - option := new(option) - option.ID = instructor.ID - option.String = instructor.LastName + ", " + instructor.FirstName + ": " + fmt.Sprint(instructor.PersonnelID) - data.Options = append(data.Options, *option) - } + data := new(httpData) + data.SessionID = s.ID template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "content", data) } } -func generateUUID() (string, error) { +func generateLogin() (string, error) { bs := make([]byte, 4) if _, err := rand.Read(bs); err != nil { - return "", fmt.Errorf("GenerateUUID: rand.Read(bs): %v\n", err) + return "", fmt.Errorf("generateLogin: rand.Read(bs): %v\n", err) } return hex.EncodeToString(bs), nil } -func AddParticipant(s *types.Session) http.HandlerFunc { +func HandleNewParticipant(s *types.Session, cp chan<- *types.Participant) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { type httpData struct { SessionID uuid.UUID @@ -94,59 +91,55 @@ func AddParticipant(s *types.Session) http.HandlerFunc { data := new(httpData) var err error - data.SessionID = s.ID - data.Login, err = generateUUID() + p := new(types.Participant) + p.Login, err = generateLogin() if err != nil { - http.Error(w, "AddParticipant: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError) + http.Error(w, "AddParticipant: generateLogin(): "+fmt.Sprint(err), http.StatusInternalServerError) + } + s.Participants = append(s.Participants, p) + cp <- p + + data.SessionID = s.ID + data.Login = p.Login + if err != nil { + http.Error(w, "AddParticipant: generateLogin(): "+fmt.Sprint(err), http.StatusInternalServerError) } - s.Logins = append(s.Logins, data.Login) template.Must(template.ParseFiles("templates/briefing.html")).ExecuteTemplate(w, "new", data) } } -func SubmitBriefingForm(s *types.Session, db *db.DB) http.HandlerFunc { +func HandleBriefingForm(s *types.Session, db *db.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { now := time.Now() - briefing := new(types.Briefing) var err error - briefing.Date = now.Format("2006-01-02") - briefing.Time = now.Format("15:04:05") - briefing.Location = r.PostFormValue("location") - briefing.DocumentName = r.PostFormValue("document-name") - briefing.AsOf = r.PostFormValue("as-of") + s.Briefing.Date = now.Format("2006-01-02") + s.Briefing.Time = now.Format("15:04:05") + s.Briefing.Location = r.PostFormValue("location") + s.Briefing.DocumentName = r.PostFormValue("document") + s.Briefing.AsOf = r.PostFormValue("as-of") - briefing.InstructorID, err = strconv.ParseInt(r.PostFormValue("instructor"), 10, 64) - if err != nil { - http.Error(w, "SubmitBriefingForm: strconv.ParseInt(): "+fmt.Sprint(err), http.StatusInternalServerError) - log.Panicln(err) - } - - err = db.WriteBriefing(briefing) + err = db.WriteBriefing(s.Briefing) if err != nil { http.Error(w, "SubmitBriefingForm: db.WriteBriefing(): "+fmt.Sprint(err), http.StatusInternalServerError) log.Panicln(err) } - s.BriefingID = briefing.ID - s.InstructorID = briefing.InstructorID - displayTable(w, db) } } // TODO: Make it only serve one purpose -func loginIsCorrect(l string, ss []*types.Session) bool { - for _, session := range ss { - for i, v := range session.Logins { - if l == v { - session.Logins = append(session.Logins[:i], session.Logins[i+1:]...) - return true +func findCorrectLogin(l string, ss *[]*types.Session) (*types.Session, *types.Participant, bool) { + for _, session := range *ss { + for _, p := range session.Participants { + if l == p.Login { + return session, p, true } } } - return false + return nil, nil, false } func newParticipant(l string) (*types.Participant, error) { @@ -161,21 +154,18 @@ func newParticipant(l string) (*types.Participant, error) { return p, nil } -func DisplayParticipantForm(ss []*types.Session) http.HandlerFunc { +func HandleExternalLogin(ss *[]*types.Session) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { type httpData struct { SessionID uuid.UUID - UUID string + Login string } - if loginIsCorrect(r.PostFormValue("login"), ss) { + session, participant, loginCorrect := findCorrectLogin(r.PostFormValue("login"), ss) + if loginCorrect { data := new(httpData) - var err error - - data.UUID, err = generateUUID() - if err != nil { - http.Error(w, "DisplayParticipantForm: generateUUID(): "+fmt.Sprint(err), http.StatusInternalServerError) - } + data.SessionID = session.ID + data.Login = participant.Login template.Must(template.ParseFiles("templates/participant.html")).ExecuteTemplate(w, "content", data) } else { @@ -184,66 +174,156 @@ func DisplayParticipantForm(ss []*types.Session) http.HandlerFunc { } } -// 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(): %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) -// } -// } +func handleGivenAnswer(s *types.Session, p *types.Participant, i int64, r *http.Request, db *db.DB) error { + answer, err := strconv.Atoi(r.PostFormValue("answer")) + if err != nil { + return fmt.Errorf("handleGivenAnswer: strconv.Atoi(): %v\n", err) + } + + if err := db.WriteGivenAnswer(s.Briefing, p, &s.Questions[i], answer); err != nil { + return fmt.Errorf("handleGivenAnswer: db.WriteGivenAnswer(): %v\n", err) + } + + return nil +} + +func HandleParticipant(s *types.Session, p *types.Participant, sq *[]types.Question, db *db.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + type httpData struct { + SessionID uuid.UUID + Login string + Question types.Question + QuestionID int64 + } + + p.FirstName = r.PostFormValue("first-" + fmt.Sprint(p.Login)) + p.LastName = r.PostFormValue("last-" + fmt.Sprint(p.Login)) + p.Company = r.PostFormValue("company-" + fmt.Sprint(p.Login)) + + err := db.WriteParticipant(p) + if err != nil { + http.Error(w, "DisplayQuestion: db.WriteParticipant(): "+fmt.Sprint(err), http.StatusInternalServerError) + log.Panicln(err) + } + + data := new(httpData) + data.SessionID = s.ID + data.Login = p.Login + data.Question = (*sq)[0] + data.QuestionID = 1 + + template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data) + } +} + +func HandleAnswer(s *types.Session, db *db.DB, p *types.Participant, sq *[]types.Question, i int64) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + log.Println(i, len(*sq)) + if i < int64(len(*sq)) { + type httpData struct { + SessionID uuid.UUID + Login string + Question types.Question + QuestionID int64 + } + + if err := handleGivenAnswer(s, p, i-1, r, db); err != nil { + http.Error(w, "DisplayQuestion: handleGivenAnswer(): "+fmt.Sprint(err), http.StatusInternalServerError) + log.Panicln(err) + } + + data := new(httpData) + data.SessionID = s.ID + data.Login = p.Login + data.Question = (*sq)[i] + data.QuestionID = i + 1 + + template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data) + } else { + type answer struct { + Text string + Correct bool + Chosen bool + } + + type question struct { + Text string + Answers []answer + } + + type httpData struct { + SessionID uuid.UUID + Login string + Questions []question + Incorrect int + } + + if err := handleGivenAnswer(s, p, i-1, r, db); err != nil { + http.Error(w, "DisplayTestResults: handleGivenAnswer(): "+fmt.Sprint(err), http.StatusInternalServerError) + log.Panicln(err) + } + + givenAnswers, err := db.GetGivenAnswers(s.Briefing.ID, p.ID, s.Questions) + if err != nil { + http.Error(w, "DisplayTestResults: db.GetGivenAnswers(): "+fmt.Sprint(err), http.StatusInternalServerError) + log.Panicln(err) + } + + data := new(httpData) + data.SessionID = s.ID + data.Login = p.Login + data.Incorrect = 0 + + data.Questions = make([]question, 0) + for i, q := range s.Questions { + question := new(question) + question.Text = q.Text + + question.Answers = make([]answer, 0) + for j, a := range q.Answers { + answer := new(answer) + answer.Text = a.Text + + if j+1 == q.Correct { + answer.Correct = true + } else { + answer.Correct = false + } + + if j+1 == givenAnswers[i] { + answer.Chosen = true + } else { + answer.Chosen = false + } + question.Answers = append(question.Answers, *answer) + } + data.Questions = append(data.Questions, *question) + + if givenAnswers[i] != q.Correct { + data.Incorrect++ + } + } + + template.Must(template.ParseFiles("templates/results.html")).ExecuteTemplate(w, "content", data) + } + } +} + +func HandleRetry(s *types.Session, p *types.Participant, sq *[]types.Question) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + type httpData struct { + SessionID uuid.UUID + Login string + Question types.Question + QuestionID int64 + } + + data := new(httpData) + data.SessionID = s.ID + data.Login = p.Login + data.Question = (*sq)[0] + data.QuestionID = 1 + + template.Must(template.ParseFiles("templates/question.html")).ExecuteTemplate(w, "content", data) + } +} diff --git a/packages/types/types.go b/packages/types/types.go index 5c9c84d..feedc88 100644 --- a/packages/types/types.go +++ b/packages/types/types.go @@ -8,14 +8,12 @@ type Person struct { LastName string } -type Instructor struct { - Person - PersonnelID int -} +type Instructor Person type Participant struct { Person Company string + Login string } type Briefing struct { @@ -61,10 +59,8 @@ type OverviewTableData struct { } type Session struct { - ID uuid.UUID - InstructorID int64 - BriefingID int64 - Logins []string - ParticipantIDs []int64 - QuestionIDs []int64 + ID uuid.UUID + *Briefing + Participants []*Participant + Questions []Question } diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..6c1264f --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,7 @@ +.correct { + color: #00ff00; +} + +.incorrect { + color: #ff0000; +} diff --git a/templates/briefing.html b/templates/briefing.html index 6ee3cfc..87861bd 100644 --- a/templates/briefing.html +++ b/templates/briefing.html @@ -1,10 +1,10 @@ {{ define "add-buttons" }}
@@ -17,28 +17,19 @@ {{ define "content" }}