184 lines
4.8 KiB
Go
184 lines
4.8 KiB
Go
package components
|
|
|
|
import (
|
|
"time"
|
|
|
|
"charm.land/bubbles/v2/viewport"
|
|
"charm.land/lipgloss/v2"
|
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
|
"github.com/LazyBachelor/LazyPM/internal/style"
|
|
)
|
|
|
|
type IssueDetail struct {
|
|
viewport viewport.Model
|
|
issue models.Issue
|
|
comments []*models.Comment
|
|
dependencies []*models.Issue
|
|
focused bool
|
|
}
|
|
|
|
func NewIssueDetail() IssueDetail {
|
|
vp := viewport.New(viewport.WithWidth(0), viewport.WithHeight(0))
|
|
return IssueDetail{
|
|
viewport: vp,
|
|
}
|
|
}
|
|
|
|
func (i *IssueDetail) SetIssue(issue models.Issue) {
|
|
i.issue = issue
|
|
i.refreshContent()
|
|
}
|
|
|
|
// SetComments updates the list of comments displayed for the current issue.
|
|
func (i *IssueDetail) SetComments(comments []*models.Comment) {
|
|
i.comments = comments
|
|
i.refreshContent()
|
|
}
|
|
|
|
// SetDependencies updates the list of dependencies displayed for the current issue.
|
|
func (i *IssueDetail) SetDependencies(deps []*models.Issue) {
|
|
i.dependencies = deps
|
|
i.refreshContent()
|
|
}
|
|
|
|
func (i *IssueDetail) SetSize(width, height int) {
|
|
i.viewport = viewport.New(viewport.WithWidth(width), viewport.WithHeight(height))
|
|
i.refreshContent()
|
|
}
|
|
|
|
func (i *IssueDetail) SetFocused(focused bool) {
|
|
i.focused = focused
|
|
}
|
|
|
|
func (i *IssueDetail) refreshContent() {
|
|
contentWidth := max(i.viewport.Width()-2, 1)
|
|
|
|
titleRow := style.RowStyle.Render(
|
|
style.TitleStyle.Render(i.issue.Title),
|
|
)
|
|
|
|
idRow := style.RowStyle.Render(
|
|
style.LabelStyle.Render("ID:") + style.ValueStyle.Render(i.issue.ID),
|
|
)
|
|
|
|
typeRow := style.RowStyle.Render(
|
|
style.LabelStyle.Render("Type:") + style.ValueStyle.Render(string(i.issue.IssueType)),
|
|
)
|
|
|
|
statusRow := style.RowStyle.Render(
|
|
style.LabelStyle.Render("Status:") + style.StatusStyle(string(i.issue.Status)).Render(string(i.issue.Status)),
|
|
)
|
|
|
|
var closingReasonRow string
|
|
if i.issue.Status == models.StatusClosed {
|
|
var closingReason string
|
|
if i.issue.CloseReason == "" {
|
|
closingReason = "N/A"
|
|
} else {
|
|
closingReason = string(i.issue.CloseReason)
|
|
}
|
|
closingReasonRow = style.RowStyle.Render(
|
|
style.LabelStyle.Render("Close reason: ") + style.ValueStyle.Render(closingReason))
|
|
}
|
|
|
|
priorityRow := style.RowStyle.Render(
|
|
style.LabelStyle.Render("Priority:") + style.ValueStyle.Render(PriorityCodeName(i.issue.Priority)),
|
|
)
|
|
|
|
assigneeRow := style.RowStyle.Render(
|
|
style.LabelStyle.Render("Assignee:") + style.ValueStyle.Render(i.issue.Assignee),
|
|
)
|
|
|
|
dependenciesParts := i.renderDependencies()
|
|
descLabel := style.LabelStyle.Render("Description:")
|
|
descStyle := style.ValueStyle.Width(contentWidth)
|
|
descContent := descStyle.Render(i.issue.Description)
|
|
|
|
commentsLabel := style.LabelStyle.MarginTop(1).Render("Comments:")
|
|
|
|
var parts []string
|
|
parts = append(parts, titleRow, idRow, typeRow, statusRow, closingReasonRow, priorityRow, assigneeRow)
|
|
parts = append(parts, dependenciesParts...)
|
|
parts = append(parts, descLabel, descContent, commentsLabel)
|
|
parts = append(parts, i.renderComments()...)
|
|
|
|
content := lipgloss.JoinVertical(lipgloss.Left, parts...)
|
|
i.viewport.SetContent(content)
|
|
}
|
|
|
|
func formatCommentTime(t time.Time) string {
|
|
return t.Format("Jan 2, 15:04")
|
|
}
|
|
|
|
func (i IssueDetail) View() string {
|
|
content := i.viewport.View()
|
|
vpWidth := i.viewport.Width()
|
|
vpHeight := i.viewport.Height()
|
|
|
|
if i.focused {
|
|
return style.DetailsContainerStyle.
|
|
BorderForeground(style.PrimaryBorder).
|
|
Width(vpWidth).
|
|
Height(vpHeight).
|
|
MaxHeight(vpHeight).
|
|
Render(content)
|
|
}
|
|
return style.DetailsContainerStyle.
|
|
Width(vpWidth).
|
|
Height(vpHeight).
|
|
MaxHeight(vpHeight).
|
|
Render(content)
|
|
}
|
|
|
|
func (i *IssueDetail) ScrollUp(lines int) {
|
|
i.viewport.ScrollUp(lines)
|
|
}
|
|
|
|
func (i *IssueDetail) ScrollDown(lines int) {
|
|
i.viewport.ScrollDown(lines)
|
|
}
|
|
|
|
func (i *IssueDetail) renderDependencies() []string {
|
|
depsLabel := style.LabelStyle.Render("Dependencies:")
|
|
if len(i.dependencies) == 0 {
|
|
return []string{
|
|
depsLabel,
|
|
style.ValueStyle.Render(" "),
|
|
}
|
|
}
|
|
var lines []string
|
|
lines = append(lines, depsLabel)
|
|
for _, d := range i.dependencies {
|
|
if d == nil {
|
|
continue
|
|
}
|
|
depText := d.ID
|
|
if d.Title != "" {
|
|
depText += " — " + d.Title
|
|
}
|
|
lines = append(lines, style.ValueStyle.MarginLeft(1).Render(depText))
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func (i *IssueDetail) renderComments() []string {
|
|
contentWidth := max(i.viewport.Width()-4, 1)
|
|
|
|
var parts []string
|
|
if len(i.comments) == 0 {
|
|
parts = append(parts, style.ValueStyle.Render("No comments yet."))
|
|
} else {
|
|
for _, c := range i.comments {
|
|
authorDate := lipgloss.NewStyle().Foreground(style.Primary).Render(c.Author) + " " +
|
|
lipgloss.NewStyle().Foreground(style.FaintText).Render(formatCommentTime(c.CreatedAt))
|
|
commentTextStyle := style.ValueStyle.Width(contentWidth)
|
|
commentRow := lipgloss.JoinVertical(lipgloss.Left,
|
|
authorDate,
|
|
commentTextStyle.MarginLeft(1).Render(c.Text),
|
|
)
|
|
parts = append(parts, commentRow)
|
|
}
|
|
}
|
|
return parts
|
|
}
|