diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index fbb3710..52447b9 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -39,6 +39,7 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) { // Issues. mux.HandleFunc("GET /issues", h.ListIssues) mux.HandleFunc("GET /issues/new", h.NewIssue) + mux.HandleFunc("GET /issues/new/labels", h.NewIssueLabels) mux.HandleFunc("POST /issues", h.CreateIssue) mux.HandleFunc("POST /issues/{owner}/{repo}/{index}/labels", h.ApplyLabels) mux.HandleFunc("POST /issues/{owner}/{repo}/{index}/close", h.CloseIssue) @@ -567,6 +568,38 @@ func (h *Handler) NewIssue(w http.ResponseWriter, r *http.Request) { renderPage(w, r, "New Issue", "issues", buf.String()) } +// NewIssueLabels handles GET /issues/new/labels — returns label checkboxes for a repo. +func (h *Handler) NewIssueLabels(w http.ResponseWriter, r *http.Request) { + token := getToken(r) + owner := r.URL.Query().Get("owner") + repo := r.URL.Query().Get("repo") + + if owner == "" || repo == "" { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, `Select a repository first.`) + return + } + + labels, err := h.Client.GetRepoLabels(r.Context(), token, owner, repo) + if err != nil { + slog.Error("failed to fetch labels", "error", err, "owner", owner, "repo", repo) + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, `Error loading labels.`) + return + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if len(labels) == 0 { + fmt.Fprint(w, `No labels available for this repository.`) + return + } + + for _, l := range labels { + fmt.Fprintf(w, ``, + l.ID, template.HTMLEscapeString(l.Color), template.HTMLEscapeString(l.Color), template.HTMLEscapeString(l.Name)) + } +} + // CreateIssue handles POST /issues. func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) { token := getToken(r) @@ -580,6 +613,15 @@ func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) { title := r.FormValue("title") body := r.FormValue("body") + // Parse label IDs from form checkboxes. + var labelIDs []int64 + for _, idStr := range r.Form["label_ids"] { + id, err := strconv.ParseInt(idStr, 10, 64) + if err == nil { + labelIDs = append(labelIDs, id) + } + } + if owner == "" || repo == "" || title == "" { if isHTMX(r) { w.Header().Set("Content-Type", "text/html; charset=utf-8") @@ -591,7 +633,7 @@ func (h *Handler) CreateIssue(w http.ResponseWriter, r *http.Request) { return } - issue, err := h.Client.CreateIssue(r.Context(), token, owner, repo, title, body, nil) + issue, err := h.Client.CreateIssue(r.Context(), token, owner, repo, title, body, labelIDs) if err != nil { slog.Error("failed to create issue", "error", err) if isHTMX(r) { diff --git a/internal/templates/create_issue.html b/internal/templates/create_issue.html index f317de6..6668a00 100644 --- a/internal/templates/create_issue.html +++ b/internal/templates/create_issue.html @@ -20,6 +20,11 @@ + +
@@ -52,7 +57,19 @@ } } - repoSelect.addEventListener('change', splitOwnerRepo); + repoSelect.addEventListener('change', function() { + splitOwnerRepo(); + var labelSection = document.getElementById('label-section'); + var labelList = document.getElementById('label-list'); + if (ownerInput.value && repoInput.value) { + labelList.innerHTML = 'Loading labels...'; + labelSection.style.display = 'block'; + htmx.ajax('GET', '/issues/new/labels?owner=' + encodeURIComponent(ownerInput.value) + '&repo=' + encodeURIComponent(repoInput.value), {target: '#label-list', swap: 'innerHTML'}); + } else { + labelSection.style.display = 'none'; + labelList.innerHTML = ''; + } + }); // Validate before HTMX submit. document.getElementById('create-issue-form').addEventListener('htmx:configRequest', function(evt) {