package handlers import ( "demoon/config" "demoon/internal/database/repos" "demoon/internal/models" "html/template" "log/slog" "net/http" "os" "strconv" ) // Handlers structure type Handlers struct { cfg config.Config log *slog.Logger repo repos.FullRepo } // NewHandlers constructor func NewHandlers( cfg config.Config, l *slog.Logger, repo repos.FullRepo, ) *Handlers { if l == nil { l = slog.New(slog.NewJSONHandler(os.Stdout, nil)) } h := &Handlers{ cfg: cfg, log: l, repo: repo, } return h } func (h *Handlers) Ping(w http.ResponseWriter, r *http.Request) { h.log.Info("got ping request") w.Write([]byte("pong")) } func (h *Handlers) HandleAnswer(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { h.log.Error("failed to parse form", "error", err) abortWithError(w, "Invalid form data") return } selectedIndex := r.FormValue("selected") questionID := r.FormValue("question_id") if selectedIndex == "" || questionID == "" { h.log.Error("missing form values", "selected", selectedIndex, "question_id", questionID) abortWithError(w, "Missing required fields") return } h.log.Info("answer received", "selected", selectedIndex, "question_id", questionID) question, err := h.repo.DBGetQuestion(questionID) if err != nil { h.log.Error("failed to get question", "error", err, "question_id", questionID) abortWithError(w, "Question not found") return } // Convert selectedIndex to int for comparison selectedIdx, err := strconv.Atoi(selectedIndex) if err != nil { h.log.Error("invalid selected index", "error", err, "selected", selectedIndex) abortWithError(w, "Invalid answer selection") return } // Validate selected index is within bounds if selectedIdx < 0 || selectedIdx > 3 { h.log.Error("selected index out of bounds", "selected", selectedIndex) abortWithError(w, "Invalid answer selection") return } selectedIdx++ // in db index starts from 1 feedback := "" if selectedIdx == int(question.CorrectIndex) { question.Status = 1 feedback = `
Correct! 🎉
` } else { question.Status = 2 feedback = `
Wrong answer! The correct answer was: ` + getCorrectOption(question) + `
` } w.Header().Set("Content-Type", "text/html") w.Write([]byte(feedback)) } func getCorrectOption(q *models.Question) string { switch q.CorrectIndex { case 0: return q.Option1 case 1: return q.Option2 case 2: return q.Option3 case 3: return q.Option4 default: return "" } } func (h *Handlers) HandleNextQuestion(w http.ResponseWriter, r *http.Request) { currentID := r.URL.Query().Get("current_id") nextID, _ := strconv.Atoi(currentID) nextID++ question, err := h.repo.DBGetQuestion(strconv.Itoa(nextID)) if err != nil { h.log.Error("failed to get next question", "error", err) abortWithError(w, "No more questions") return } h.renderQuestion(w, question) } func (h *Handlers) renderQuestion(w http.ResponseWriter, question *models.Question) { tmpl, err := template.ParseGlob("components/*.html") if err != nil { abortWithError(w, err.Error()) return } // Add ShowNext flag to template data type TemplateData struct { *models.Question } err = tmpl.ExecuteTemplate(w, "main", &TemplateData{ Question: question, ShowNext: h.showNext, }) if err != nil { h.log.Error("failed to render template", "error", err) } question, err = h.repo.DBGetQuestion("1") if err != nil { h.log.Error("failed to get question", "error", err) abortWithError(w, "Question not found") return } } func (h *Handlers) MainPage(w http.ResponseWriter, r *http.Request) { question, err := h.repo.DBGetQuestion("1") if err != nil { h.log.Error("failed to get question", "error", err) abortWithError(w, "Question not found") return } h.renderQuestion(w, question) }