Files
LazyPM/internal/models/beads.go
Robin Olsen d900a5fcd3 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.
2026-03-23 19:26:04 +01:00

160 lines
4.1 KiB
Go

package models
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"unicode"
"charm.land/lipgloss/v2"
"github.com/muesli/reflow/truncate"
"github.com/steveyegge/beads"
)
type (
Issue = beads.Issue
Status = beads.Status
IssueType = beads.IssueType
Dependency = beads.Dependency
DependencyType = beads.DependencyType
Label = beads.Label
Comment = beads.Comment
Event = beads.Event
EventType = beads.EventType
BlockedIssue = beads.BlockedIssue
TreeNode = beads.TreeNode
IssueFilter = beads.IssueFilter
WorkFilter = beads.WorkFilter
StaleFilter = beads.StaleFilter
DependencyCounts = beads.DependencyCounts
IssueWithCounts = beads.IssueWithCounts
SortPolicy = beads.SortPolicy
EpicStatus = beads.EpicStatus
)
// Status constants
const (
StatusOpen = beads.StatusOpen
StatusInProgress = beads.StatusInProgress
StatusBlocked = beads.StatusBlocked
StatusDeferred = beads.StatusDeferred
StatusClosed = beads.StatusClosed
)
// IssueType constants
const (
TypeBug = beads.TypeBug
TypeFeature = beads.TypeFeature
TypeTask = beads.TypeTask
TypeEpic = beads.TypeEpic
TypeChore = beads.TypeChore
)
// DependencyType constants
const (
DepBlocks = beads.DepBlocks
DepRelated = beads.DepRelated
DepParentChild = beads.DepParentChild
DepDiscoveredFrom = beads.DepDiscoveredFrom
DepConditionalBlocks = beads.DepConditionalBlocks // B runs only if A fails (bd-kzda)
)
// SortPolicy constants
const (
SortPolicyHybrid = beads.SortPolicyHybrid
SortPolicyPriority = beads.SortPolicyPriority
SortPolicyOldest = beads.SortPolicyOldest
)
// EventType constants
const (
EventCreated = beads.EventCreated
EventUpdated = beads.EventUpdated
EventStatusChanged = beads.EventStatusChanged
EventCommented = beads.EventCommented
EventClosed = beads.EventClosed
EventReopened = beads.EventReopened
EventDependencyAdded = beads.EventDependencyAdded
EventDependencyRemoved = beads.EventDependencyRemoved
EventLabelAdded = beads.EventLabelAdded
EventLabelRemoved = beads.EventLabelRemoved
EventCompacted = beads.EventCompacted
)
func IssueString(issue Issue) string {
labelStyle := lipgloss.NewStyle().
Bold(true).Foreground(lipgloss.Color("5"))
titleStyle := lipgloss.NewStyle().
Bold(true).Foreground(lipgloss.Color("6"))
boxStyle := lipgloss.NewStyle().Padding(1)
line := func(label, value string) string {
return labelStyle.Render(label) + ": " + value + "\t"
}
content := lipgloss.JoinVertical(
lipgloss.Left,
line("Title", titleStyle.Render(issue.Title)+"\t"+line("Assignee", issue.Assignee)),
line("ID", issue.ID+"\t"+line("Type", string(issue.IssueType))+line("Status", string(issue.Status))+line("Priority", fmt.Sprintf("%d", issue.Priority))),
line("Description", "\n"+issue.Description),
)
return boxStyle.Render(content)
}
func IssuesPtrToIssues(issuePtr []*Issue) []Issue {
issues := make([]Issue, 0, len(issuePtr))
for _, issuePtr := range issuePtr {
if issuePtr != nil {
issues = append(issues, *issuePtr)
}
}
return issues
}
func sanitizeCell(s string) string {
s = strings.Map(func(r rune) rune {
switch r {
case '\n', '\r', '\t':
return ' '
default:
return r
}
}, s)
return strings.Join(strings.FieldsFunc(s, unicode.IsSpace), " ")
}
func FormatIssueRow(issue Issue) string {
id := truncate.String(sanitizeCell(issue.ID), 10)
title := truncate.StringWithTail(sanitizeCell(issue.Title), 35, "...")
description := truncate.StringWithTail(sanitizeCell(issue.Description), 40, "...")
status := sanitizeCell(string(issue.Status))
issueType := sanitizeCell(string(issue.IssueType))
return fmt.Sprintf(
"%s\t%s\t%s\t%s\t%s\t%d",
id,
title,
description,
status,
issueType,
issue.Priority,
)
}
func PrintIssues(issues []Issue) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\tTITLE\tDESCRIPTION\tSTATUS\tTYPE\tPRIORITY")
for _, issue := range issues {
fmt.Fprintln(w, FormatIssueRow(issue))
}
_ = w.Flush()
}