Merge pull request #75 from LazyBachelor/LPM-140
LPM-140 The Sprinting Update Adds sprint support across the web UI (board + issue detail), TUI kanban, CLI/REPL, and the Beads-backed storage layer. Changes: Introduces sprint entities in storage and exposes sprint operations via IssueService. Updates web board to support backlog vs. sprint columns, sprint selection, and sprint creation routes. Updates TUI kanban to show backlog + sprint columns and adds sprint selection/creation actions.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -21,24 +22,72 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sort issues by highest priority first (4 -> 0)
|
||||
sort.Slice(issues, func(i, j int) bool {
|
||||
return issues[i].Priority > issues[j].Priority
|
||||
})
|
||||
|
||||
// Check if board view is requested
|
||||
isBoardView := r.URL.Query().Get("board") == "true"
|
||||
isBoardView := r.URL.Query().Get("board") == "true" || strings.HasPrefix(r.URL.Path, "/board/")
|
||||
|
||||
if isBoardView {
|
||||
ctx := r.Context()
|
||||
|
||||
backlogIssues, err := app.Issues.GetIssuesNotInAnySprint(ctx)
|
||||
if err != nil {
|
||||
backlogIssues = []*models.Issue{}
|
||||
}
|
||||
|
||||
sort.Slice(backlogIssues, func(i, j int) bool {
|
||||
return backlogIssues[i].Priority > backlogIssues[j].Priority
|
||||
})
|
||||
|
||||
sprints, err := app.Issues.GetSprints(ctx)
|
||||
if err != nil {
|
||||
sprints = []int{}
|
||||
}
|
||||
|
||||
currentSprint := 0
|
||||
sprintParam := r.URL.Query().Get("sprint")
|
||||
if sprintParam != "" {
|
||||
fmt.Sscanf(sprintParam, "%d", ¤tSprint)
|
||||
}
|
||||
|
||||
if currentSprint == 0 && len(sprints) > 0 {
|
||||
currentSprint = sprints[0]
|
||||
}
|
||||
|
||||
var sprintIssues []*models.Issue
|
||||
if currentSprint > 0 {
|
||||
sprintIssues, err = app.Issues.GetIssuesBySprint(ctx, currentSprint)
|
||||
if err != nil {
|
||||
sprintIssues = []*models.Issue{}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(sprintIssues, func(i, j int) bool {
|
||||
return sprintIssues[i].Priority > sprintIssues[j].Priority
|
||||
})
|
||||
|
||||
boardProps := routes.BoardViewProps{
|
||||
BaseURL: "/?board=true",
|
||||
QueryParam: query,
|
||||
Issues: issues,
|
||||
EmptyText: "No issues found",
|
||||
BaseURL: "/?board=true",
|
||||
QueryParam: query,
|
||||
Issues: issues,
|
||||
BacklogIssues: backlogIssues,
|
||||
SprintIssues: sprintIssues,
|
||||
CurrentSprint: currentSprint,
|
||||
Sprints: sprints,
|
||||
EmptyText: "No issues found",
|
||||
}
|
||||
|
||||
if hx.IsHxRequest() {
|
||||
routes.BoardViewContent(boardProps).Render(r.Context(), w)
|
||||
if r.URL.Query().Get("board") != "true" {
|
||||
w.Header().Set("HX-Push-Url", fmt.Sprintf("/?board=true&sprint=%d", currentSprint))
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, "/board/") {
|
||||
routes.BoardColumns(boardProps).Render(r.Context(), w)
|
||||
} else {
|
||||
routes.BoardViewContent(boardProps).Render(r.Context(), w)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,7 +95,6 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Regular dashboard view
|
||||
var selectedIssue *models.Issue
|
||||
if len(issues) > 0 {
|
||||
selectedIssue = issues[0]
|
||||
@@ -62,6 +110,13 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if selectedIssue != nil {
|
||||
comments, err := app.Issues.GetIssueComments(r.Context(), selectedIssue.ID)
|
||||
if err == nil && comments != nil {
|
||||
selectedIssue.Comments = comments
|
||||
}
|
||||
}
|
||||
|
||||
isPartial := r.URL.Query().Get("partial") == "true"
|
||||
if hx.IsHxRequest() && isPartial {
|
||||
routes.DashboardIssueList(issues, selectedIssueID).Render(r.Context(), w)
|
||||
@@ -83,3 +138,18 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
routes.Dashboard(props).Render(r.Context(), w)
|
||||
}
|
||||
|
||||
func CreateSprintHandler(w http.ResponseWriter, r *http.Request) {
|
||||
app := App(r)
|
||||
ctx := r.Context()
|
||||
|
||||
sprintNum, err := app.Issues.AddSprint(ctx)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to create sprint", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL := fmt.Sprintf("/?board=true&sprint=%d", sprintNum)
|
||||
w.Header().Set("HX-Redirect", redirectURL)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user