refactor for better reusability and maintainability

This commit is contained in:
Robin Olsen
2026-02-17 15:17:49 +01:00
parent e2f3aa63da
commit 86c8bc3143
6 changed files with 140 additions and 48 deletions

View File

@@ -23,7 +23,7 @@ func initializeServices(ctx context.Context) (*service.Services, func(), error)
func initTasks(svc *service.Services) []*task.Task {
return []*task.Task{
tasks.NewCreateIssueTask(svc),
tasks.NewCreateIssueTask(svc).Init(),
}
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"math/rand"
"github.com/LazyBachelor/LazyPM/cmd/survey/tasks"
"github.com/LazyBachelor/LazyPM/pkg/task"
)
@@ -15,6 +16,7 @@ func taskLoop(ctx context.Context, surveyTasks []*task.Task, interfaces []task.I
for _, t := range surveyTasks {
t.SetInterface(interfaces[interfaceIndex])
t.SetInterfaceType(tasks.InterfaceToType(interfaces[interfaceIndex]))
doneChan := make(chan bool, 1)
quitChan := make(chan bool, 1)

View File

@@ -17,18 +17,26 @@ This task will test your ability to use the issue creation workflow effectively.
Assign this task to yourself and start creating the issue.
Make sure to fill out all the necessary details, including the title, description, and assignee.`
func NewCreateIssueTask(svc *service.Services) *task.Task {
aboutScreen := ui.NewTaskModel(createIssueDetails())
questionnaire := ui.NewQuestionnaireModel(createIssueQuestionnaire())
type CreateIssueTask struct {
*task.Task
svc *service.Services
}
task := task.NewTask(svc, aboutScreen, questionnaire)
task.SetConfigFunc(createIssueConfig)
task.SetDbStateFunc(createIssueDbState)
task.SetValidateFunc(createIssueValidate)
func NewCreateIssueTask(svc *service.Services) *CreateIssueTask {
return &CreateIssueTask{
svc: svc,
}
}
func (t *CreateIssueTask) Init() *task.Task {
task := task.NewTask(t.svc, t.Details(), t.QuestionsFunc())
task.SetConfigFunc(t.Config)
task.SetDbStateFunc(t.DbStateFunc)
task.SetValidateFunc(t.ValidateFunc)
return task
}
func createIssueConfig() task.TaskConfig {
func (t *CreateIssueTask) Config() task.TaskConfig {
return task.TaskConfig{
IssuePrefix: "pm",
BeadsDBPath: "./.pm/db.db",
@@ -37,7 +45,7 @@ func createIssueConfig() task.TaskConfig {
}
}
func createIssueDetails() ui.TaskDetails {
func (t *CreateIssueTask) Details() ui.TaskDetails {
return ui.TaskDetails{
Title: "Create Issue Task",
Description: "Create a new issue in the project management system to test the issue creation workflow.",
@@ -46,23 +54,38 @@ func createIssueDetails() ui.TaskDetails {
}
}
func createIssueQuestionnaire() ui.Questions {
return ui.Questions{
huh.NewGroup(
huh.NewConfirm().Title("Was this good"),
),
huh.NewGroup(
huh.NewSelect[int]().Options(
huh.NewOption("Very good", 1),
huh.NewOption("Very Bad", 2),
).Title("How good was it?"),
),
func (t *CreateIssueTask) QuestionsFunc() task.QuestionsFunc {
return func(interfaceType task.InterfaceType) ui.Questions {
questions := ui.Questions{}
// Add an extra question for TUI
if interfaceType == task.InterfaceTUI {
questions = append(questions,
huh.NewGroup(
huh.NewConfirm().Title("Did you complete?"),
),
)
}
questions = append(questions,
huh.NewGroup(
huh.NewConfirm().Title("Was this good"),
),
huh.NewGroup(
huh.NewSelect[int]().Options(
huh.NewOption("Very good", 1),
huh.NewOption("Very Bad", 2),
).Title("How good was it?"),
),
)
return questions
}
}
func createIssueDbState(ctx context.Context, svc *service.Services) error {
func (t *CreateIssueTask) DbStateFunc(ctx context.Context) error {
// Clear existing issues to ensure a clean state for the task
if err := svc.DeleteIssues(); err != nil {
if err := t.svc.DeleteIssues(); err != nil {
return err
}
@@ -72,16 +95,16 @@ func createIssueDbState(ctx context.Context, svc *service.Services) error {
IssueType: models.TypeTask, Status: models.StatusOpen,
}
if err := svc.Beads.CreateIssue(ctx, &issue, ""); err != nil {
if err := t.svc.Beads.CreateIssue(ctx, &issue, ""); err != nil {
return err
}
return nil
}
func createIssueValidate(ctx context.Context, svc *service.Services) (ok bool, errorMsg error) {
func (t *CreateIssueTask) ValidateFunc(ctx context.Context) (ok bool, errorMsg error) {
// Fetches issues, indexed with latest first
issues, err := svc.Beads.SearchIssues(ctx, "", models.IssueFilter{})
issues, err := t.svc.Beads.SearchIssues(ctx, "", models.IssueFilter{})
if err != nil {
return false, err
}

27
cmd/survey/tasks/types.go Normal file
View File

@@ -0,0 +1,27 @@
package tasks
import (
"github.com/LazyBachelor/LazyPM/pkg/cli/repl"
"github.com/LazyBachelor/LazyPM/pkg/task"
"github.com/LazyBachelor/LazyPM/pkg/tui"
"github.com/LazyBachelor/LazyPM/pkg/web"
)
const (
InterfaceTUI task.InterfaceType = "tui"
InterfaceCLI task.InterfaceType = "repl"
InterfaceWeb task.InterfaceType = "web"
)
func InterfaceToType(it task.Interface) task.InterfaceType {
switch it.(type) {
case *repl.REPL:
return InterfaceCLI
case *tui.Tui:
return InterfaceTUI
case *web.Web:
return InterfaceWeb
default:
return "unknown"
}
}

View File

@@ -6,38 +6,57 @@ import (
"time"
"github.com/LazyBachelor/LazyPM/internal/service"
taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui"
tea "github.com/charmbracelet/bubbletea"
)
type Tasker interface {
Init() *Task
Config() TaskConfig
Details() taskui.TaskDetails
QuestionsFunc() QuestionsFunc
InterfaceType() Interface
ValidateFunc(context.Context) (ok bool, errorMsg error)
DbStateFunc(context.Context) error
}
type Task struct {
Config TaskConfig
interfaceType Interface
aboutScreen tea.Model
questionnaire tea.Model
svc *service.Services
Config TaskConfig
Interface Interface
InterfaceType InterfaceType
details taskui.TaskDetails
questionsFunc QuestionsFunc
validateFunc ValidateFunc
dbStateFunc DbStateFunc
svc *service.Services
feedbackChan chan ValidationFeedback
doneChan chan bool
quitChan chan bool
}
func NewTask(svc *service.Services, aboutScreen tea.Model, questionnaire tea.Model) *Task {
func NewTask(svc *service.Services, details taskui.TaskDetails, questionsFunc QuestionsFunc) *Task {
return &Task{
aboutScreen: aboutScreen,
questionnaire: questionnaire,
details: details,
questionsFunc: questionsFunc,
svc: svc,
}
}
func (t *Task) IntroduceTask() error {
if t.aboutScreen == nil {
return fmt.Errorf("aboutScreen is not set")
if t.details == (taskui.TaskDetails{}) {
return fmt.Errorf("details is not set")
}
model, err := tea.NewProgram(t.aboutScreen, tea.WithAltScreen()).Run()
detailsScreen := taskui.NewTaskModel(t.details)
model, err := tea.NewProgram(detailsScreen, tea.WithAltScreen()).Run()
if err != nil {
return err
}
@@ -48,32 +67,39 @@ func (t *Task) IntroduceTask() error {
}
func (t *Task) StartInterface(ctx context.Context, cfg TaskConfig) error {
if t.interfaceType == nil {
if t.Interface == nil {
return fmt.Errorf("interfaceType is not set")
}
return t.interfaceType.Run(ctx, cfg)
return t.Interface.Run(ctx, cfg)
}
func (t *Task) Initialize(ctx context.Context) error {
if t.dbStateFunc == nil {
return fmt.Errorf("dbStateFunc is not set")
}
return t.dbStateFunc(ctx, t.svc)
return t.dbStateFunc(ctx)
}
func (t *Task) Validate(ctx context.Context) (bool, error) {
if t.validateFunc == nil {
return false, fmt.Errorf("validateFunc is not set")
}
return t.validateFunc(ctx, t.svc)
return t.validateFunc(ctx)
}
func (t *Task) StartQuestionnaire() error {
if t.questionnaire == nil {
return fmt.Errorf("questionnaire is not set")
if t.questionsFunc == nil {
return fmt.Errorf("questionsFunc is not set")
}
model, err := tea.NewProgram(t.questionnaire, tea.WithAltScreen()).Run()
questions := t.questionsFunc(t.InterfaceType)
if questions == nil {
return fmt.Errorf("questions is nil")
}
questionare := taskui.NewQuestionnaireModel(questions)
model, err := tea.NewProgram(questionare, tea.WithAltScreen()).Run()
if err != nil {
return err
}
@@ -88,7 +114,11 @@ func (t *Task) SetConfigFunc(fn ConfigFunc) {
}
func (t *Task) SetInterface(interfaceType Interface) {
t.interfaceType = interfaceType
t.Interface = interfaceType
}
func (t *Task) SetInterfaceType(interfaceType InterfaceType) {
t.InterfaceType = interfaceType
}
func (t *Task) SetDbStateFunc(fn DbStateFunc) {

View File

@@ -6,6 +6,7 @@ import (
"time"
"github.com/LazyBachelor/LazyPM/internal/service"
taskui "github.com/LazyBachelor/LazyPM/pkg/task/ui"
)
var ErrUserQuit = errors.New("user quit")
@@ -16,9 +17,18 @@ type Interface interface {
Run(context.Context, TaskConfig) error
}
type InterfaceType string
const (
InterfaceTUI InterfaceType = "tui"
InterfaceCLI InterfaceType = "repl"
InterfaceWeb InterfaceType = "web"
)
type ConfigFunc func() TaskConfig
type ValidateFunc func(context.Context, *service.Services) (ok bool, err error)
type DbStateFunc func(context.Context, *service.Services) error
type ValidateFunc func(context.Context) (ok bool, err error)
type DbStateFunc func(context.Context) error
type QuestionsFunc func(InterfaceType) taskui.Questions
type ValidationFeedback struct {
Success bool