Enhance status component and dashboard functionality

- Updated the status component template to include a new trigger for task status checks.
- Modified the status component's Go implementation to reflect the new trigger in the HTML output.
- Improved the UpdateIssue handler to conditionally set the assignee based on form input.
- Enhanced the dashboard template to include a new dependencies section for issues, allowing users to add dependencies dynamically.
- Refactored the dashboard's issue rows to utilize JavaScript functions for rendering status, type, and priority badges, improving maintainability and readability.
This commit is contained in:
Robin Olsen
2026-04-01 14:37:31 +02:00
parent f755e9f01e
commit 2f0426b58f
14 changed files with 335 additions and 53 deletions

View File

@@ -2,7 +2,7 @@ package tasks
import (
"context"
"strings"
"fmt"
"charm.land/huh/v2"
"github.com/LazyBachelor/LazyPM/internal/models"
@@ -18,7 +18,7 @@ You need to groom the backlog:
2. Find two issues that have the same name.
3. Open one of these issues.
4. Select "Close issue"
5. Choose "Duplicate issue" as closing reason.
5. Set the closing reason to "Duplicate issue".
6. Save/close issue.
Focus on making the backlog a reliable source of upcoming work.`
@@ -126,17 +126,31 @@ func (t *BacklogRefinementTask) Validate(ctx context.Context) ValidationFeedback
return expect.Fatal("Could not fetch issues")
}
fmt.Printf("DEBUG: Found %d issues\n", len(issues))
var closedDuplicate *models.Issue
for _, issue := range issues {
if issue.Status == models.StatusClosed &&
strings.Contains(strings.ToLower(issue.CloseReason), "duplicate") {
fmt.Printf("DEBUG: Issue '%s' has status: %v\n", issue.Title, issue.Status)
if issue.Status == models.StatusClosed {
closedDuplicate = issue
break
}
}
expect.Assert(closedDuplicate != nil,
"Expected one duplicate issue to be closed with 'Duplicate issue' as closing reason")
fmt.Printf("DEBUG: closedDuplicate is nil: %v\n", closedDuplicate == nil)
if closedDuplicate == nil {
expect.Fail("Closed duplicate issue is wrong")
} else {
expect.Pass("Closed duplicate issue is correct")
title := closedDuplicate.Title
isDuplicate := title == "User profile page" || title == "Fix login timeout"
expect.Assert(isDuplicate, "Closed issue was one of the duplicates")
}
return expect.Complete()
feedback := expect.Complete()
fmt.Printf("DEBUG: Validation Success=%v, Checks=%d\n", feedback.Success, len(feedback.Checks))
for i, check := range feedback.Checks {
fmt.Printf("DEBUG: Check[%d]: Valid=%v, Message=%s\n", i, check.Valid, check.Message)
}
return feedback
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"os"
"strings"
"time"
"charm.land/huh/v2"
"github.com/LazyBachelor/LazyPM/internal/models"
@@ -37,9 +38,10 @@ function Add(a, b int) int {
var textFileContent = codingDescription + textFileDescription + "\n" + code
type CodingTask struct {
done bool
app *App
issue *Issue
done bool
app *App
issue *Issue
lastModified time.Time
}
func NewCodingTask(app *App) *CodingTask {
@@ -89,6 +91,10 @@ func (t *CodingTask) Setup(ctx context.Context) error {
return err
}
if stat, err := os.Stat("./code.txt"); err == nil {
t.lastModified = stat.ModTime()
}
t.issue = NewIssueBuilder().
WithTitle("Fix major error").
WithDescription(desc).
@@ -96,7 +102,45 @@ func (t *CodingTask) Setup(ctx context.Context) error {
WithPriority(4).
Build()
return t.app.Issues.CreateIssue(ctx, t.issue, "")
if err := t.app.Issues.CreateIssue(ctx, t.issue, ""); err != nil {
return err
}
// Start file watcher goroutine
t.startFileWatcher(ctx)
return nil
}
func (t *CodingTask) startFileWatcher(ctx context.Context) {
go func() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if t.app.SubmitChan == nil {
continue
}
stat, err := os.Stat("./code.txt")
if err != nil {
continue
}
if stat.ModTime().After(t.lastModified) {
t.lastModified = stat.ModTime()
select {
case t.app.SubmitChan <- models.ValidationTrigger{Source: models.ValidationTriggerAutoPoll}:
default:
}
}
}
}
}()
}
func (t *CodingTask) Validate(ctx context.Context) ValidationFeedback {

View File

@@ -5,6 +5,7 @@ import (
"errors"
"os"
"strings"
"time"
"charm.land/huh/v2"
"github.com/LazyBachelor/LazyPM/internal/models"
@@ -33,10 +34,11 @@ The repository is in the task folder (./task).`
const gitTaskReadmeContent = "This is a Git task. Add your name or a short note below this line to complete it, then commit your change."
type GitTask struct {
app *App
done bool
repo *git.Repository
setupIssue *Issue
app *App
done bool
repo *git.Repository
setupIssue *Issue
lastModified time.Time
}
var gitTaskInProgress = false
@@ -102,9 +104,46 @@ func (t *GitTask) Setup(ctx context.Context) error {
return err
}
// Initialize lastModified and start file watcher
if stat, err := os.Stat("./task/README.md"); err == nil {
t.lastModified = stat.ModTime()
}
t.startFileWatcher(ctx)
return nil
}
func (t *GitTask) startFileWatcher(ctx context.Context) {
go func() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if t.app.SubmitChan == nil {
continue
}
stat, err := os.Stat("./task/README.md")
if err != nil {
continue
}
if stat.ModTime().After(t.lastModified) {
t.lastModified = stat.ModTime()
select {
case t.app.SubmitChan <- models.ValidationTrigger{Source: models.ValidationTriggerAutoPoll}:
default:
}
}
}
}
}()
}
func (t *GitTask) Validate(ctx context.Context) ValidationFeedback {
expect := check.NewExpector()
@@ -117,7 +156,7 @@ func (t *GitTask) Validate(ctx context.Context) ValidationFeedback {
return expect.ValidationFeedback
}
expect.Equal(issue.Assignee, "Me", "Issue Assignee")
expect.NotEmptyAndEqual(issue.Assignee, "Me", "Issue Assignee")
if !expect.Valid() {
return expect.ValidationFeedback