From aabce0b0b2fdfa1cc68c0a6e7090b8d9c3f99af2 Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Sat, 28 Feb 2026 23:30:31 +0100 Subject: [PATCH] refactor to use app package instead of service refactor app to be composable --- cmd/pm/main.go | 24 +++--- cmd/survey/init.go | 8 +- cmd/survey/main.go | 82 +++++++++--------- cmd/survey/runner.go | 12 +-- cmd/survey/tasks/backlogRefinement.go | 4 +- cmd/survey/tasks/base.go | 6 +- cmd/survey/tasks/codingTask.go | 4 +- cmd/survey/tasks/createIssue.go | 4 +- cmd/survey/tasks/dependencyManagement.go | 18 ++-- cmd/survey/tasks/gitTask.go | 14 ++-- cmd/survey/tasks/issueTriage.go | 4 +- cmd/survey/tasks/milestoneTracking.go | 4 +- cmd/survey/tasks/priorityManagement.go | 4 +- cmd/survey/tasks/reportGeneration.go | 4 +- cmd/survey/tasks/sprintPlanning.go | 4 +- cmd/survey/tasks/stakeholderUpdate.go | 4 +- cmd/survey/tasks/teamCapacity.go | 4 +- internal/app/app.go | 66 +++++++++++++++ internal/app/initializer.go | 41 +++++++++ internal/app/lifecycle.go | 21 +++++ internal/app/options.go | 68 +++++++++++++++ internal/{service => app}/statistics.go | 2 +- internal/commands/issues/close.go | 2 +- internal/commands/issues/comment.go | 3 +- internal/commands/issues/comments_list.go | 2 +- internal/commands/issues/completion.go | 2 +- internal/commands/issues/create.go | 10 +-- internal/commands/issues/delete.go | 2 +- internal/commands/issues/list.go | 10 +-- internal/commands/issues/read.go | 2 +- internal/commands/issues/root.go | 2 +- internal/commands/issues/update.go | 10 +-- internal/commands/survey/issues.go | 2 +- internal/commands/survey/list.go | 2 +- internal/commands/survey/root.go | 2 +- internal/commands/survey/start.go | 8 +- internal/commands/survey/status.go | 4 +- internal/commands/survey/submit.go | 2 +- internal/models/app.go | 4 +- internal/service/app.go | 87 -------------------- internal/storage/{storage.go => json.go} | 0 internal/utils/{ => browser}/browser.go | 4 +- internal/utils/{ => check}/expect.go | 4 +- internal/utils/{ => shellcomp}/completion.go | 2 +- pkg/cli/cli.go | 16 ++-- pkg/repl/executor.go | 2 +- pkg/repl/repl.go | 8 +- pkg/repl/suggestions.go | 2 +- pkg/tui/tui.go | 4 +- pkg/tui/views/dashboard/issue_list.go | 8 +- pkg/tui/views/dashboard/model.go | 16 ++-- pkg/tui/views/dashboard/operations.go | 16 ++-- pkg/tui/views/views.go | 4 +- pkg/web/handler/context.go | 8 +- pkg/web/server/server.go | 4 +- pkg/web/web.go | 8 +- 56 files changed, 385 insertions(+), 279 deletions(-) create mode 100644 internal/app/app.go create mode 100644 internal/app/initializer.go create mode 100644 internal/app/lifecycle.go create mode 100644 internal/app/options.go rename internal/{service => app}/statistics.go (98%) delete mode 100644 internal/service/app.go rename internal/storage/{storage.go => json.go} (100%) rename internal/utils/{ => browser}/browser.go (89%) rename internal/utils/{ => check}/expect.go (99%) rename internal/utils/{ => shellcomp}/completion.go (95%) diff --git a/cmd/pm/main.go b/cmd/pm/main.go index 2b1ecb0..706cd5b 100644 --- a/cmd/pm/main.go +++ b/cmd/pm/main.go @@ -3,26 +3,26 @@ package main import ( "context" - issuesCmd "github.com/LazyBachelor/LazyPM/internal/commands/issues" - surveyCmd "github.com/LazyBachelor/LazyPM/internal/commands/survey" + issues "github.com/LazyBachelor/LazyPM/internal/commands/issues" + survey "github.com/LazyBachelor/LazyPM/internal/commands/survey" "github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/pkg/cli" ) func main() { - if err := cli.New(issuesCmd.RootCmd).Run(context.Background(), models.BaseConfig); err != nil { + if err := cli.New(issues.RootCmd).Run(context.Background(), models.BaseConfig); err != nil { return } } func init() { - issuesCmd.RootCmd.AddCommand(issuesCmd.GetCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.ListCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CloseCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CreateCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.DeleteCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.UpdateCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CommentCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CommentsCmd) - issuesCmd.RootCmd.AddCommand(surveyCmd.StatusCmd) + issues.RootCmd.AddCommand(issues.GetCmd) + issues.RootCmd.AddCommand(issues.ListCmd) + issues.RootCmd.AddCommand(issues.CloseCmd) + issues.RootCmd.AddCommand(issues.CreateCmd) + issues.RootCmd.AddCommand(issues.DeleteCmd) + issues.RootCmd.AddCommand(issues.UpdateCmd) + issues.RootCmd.AddCommand(issues.CommentCmd) + issues.RootCmd.AddCommand(issues.CommentsCmd) + issues.RootCmd.AddCommand(survey.StatusCmd) } diff --git a/cmd/survey/init.go b/cmd/survey/init.go index cd8e346..e140edc 100644 --- a/cmd/survey/init.go +++ b/cmd/survey/init.go @@ -4,14 +4,14 @@ import ( "context" "github.com/LazyBachelor/LazyPM/cmd/survey/tasks" - "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/pkg/task" _ "github.com/LazyBachelor/LazyPM/cmd/survey/tasks" ) -func initializeServices(ctx context.Context) (*service.App, func(), error) { - return service.NewApp(ctx, tasks.BaseConfig().WithAutoInit(true)) +func initializeServices(ctx context.Context) (*app.App, func(), error) { + return app.New(ctx, tasks.BaseConfig().WithAutoInit(true)) } func initInterfaces() map[string]task.Interface { @@ -26,7 +26,7 @@ func initInterfaces() map[string]task.Interface { return interfaces } -func initTasks(app *service.App) map[string]task.Tasker { +func initTasks(app *app.App) map[string]task.Tasker { taskMap := make(map[string]task.Tasker) for _, name := range task.ListTasks() { t, err := task.GetTask(name, app) diff --git a/cmd/survey/main.go b/cmd/survey/main.go index 78158d3..4916a46 100644 --- a/cmd/survey/main.go +++ b/cmd/survey/main.go @@ -4,9 +4,9 @@ import ( "context" "github.com/LazyBachelor/LazyPM/cmd/survey/tasks" - issuesCmd "github.com/LazyBachelor/LazyPM/internal/commands/issues" - surveyCmd "github.com/LazyBachelor/LazyPM/internal/commands/survey" - "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/internal/app" + issues "github.com/LazyBachelor/LazyPM/internal/commands/issues" + survey "github.com/LazyBachelor/LazyPM/internal/commands/survey" "github.com/LazyBachelor/LazyPM/pkg/repl" "github.com/LazyBachelor/LazyPM/pkg/task" "github.com/LazyBachelor/LazyPM/pkg/tui" @@ -22,10 +22,10 @@ func main() { } defer cleanup() - surveyCmd.SetApp(app) - issuesCmd.SetApp(app) + survey.SetApp(app) + issues.SetApp(app) - if err := fang.Execute(ctx, surveyCmd.RootCmd, + if err := fang.Execute(ctx, survey.RootCmd, fang.WithColorSchemeFunc(fang.AnsiColorScheme)); err != nil { return } @@ -36,69 +36,69 @@ func init() { task.RegisterInterface("web", web.New()) task.RegisterInterface("repl", repl.New()) - task.RegisterTask("create_issue", func(app *service.App) task.Tasker { + task.RegisterTask("create_issue", func(app *app.App) task.Tasker { return tasks.NewCreateIssueTask(app) }) - task.RegisterTask("coding_task", func(app *service.App) task.Tasker { + task.RegisterTask("coding_task", func(app *app.App) task.Tasker { return tasks.NewCodingTask(app) }) - task.RegisterTask("git_task", func(app *service.App) task.Tasker { + task.RegisterTask("git_task", func(app *app.App) task.Tasker { return tasks.NewGitTask(app) }) - task.RegisterTask("sprint_planning", func(app *service.App) task.Tasker { + task.RegisterTask("sprint_planning", func(app *app.App) task.Tasker { return tasks.NewSprintPlanningTask(app) }) - task.RegisterTask("issue_triage", func(app *service.App) task.Tasker { + task.RegisterTask("issue_triage", func(app *app.App) task.Tasker { return tasks.NewIssueTriageTask(app) }) - task.RegisterTask("milestone_tracking", func(app *service.App) task.Tasker { + task.RegisterTask("milestone_tracking", func(app *app.App) task.Tasker { return tasks.NewMilestoneTrackingTask(app) }) - task.RegisterTask("dependency_management", func(app *service.App) task.Tasker { + task.RegisterTask("dependency_management", func(app *app.App) task.Tasker { return tasks.NewDependencyManagementTask(app) }) - task.RegisterTask("team_capacity", func(app *service.App) task.Tasker { + task.RegisterTask("team_capacity", func(app *app.App) task.Tasker { return tasks.NewTeamCapacityTask(app) }) - task.RegisterTask("report_generation", func(app *service.App) task.Tasker { + task.RegisterTask("report_generation", func(app *app.App) task.Tasker { return tasks.NewReportGenerationTask(app) }) - task.RegisterTask("stakeholder_update", func(app *service.App) task.Tasker { + task.RegisterTask("stakeholder_update", func(app *app.App) task.Tasker { return tasks.NewStakeholderUpdateTask(app) }) - task.RegisterTask("priority_management", func(app *service.App) task.Tasker { + task.RegisterTask("priority_management", func(app *app.App) task.Tasker { return tasks.NewPriorityManagementTask(app) }) - task.RegisterTask("backlog_refinement", func(app *service.App) task.Tasker { + task.RegisterTask("backlog_refinement", func(app *app.App) task.Tasker { return tasks.NewBacklogRefinementTask(app) }) // Basic survey commands - surveyCmd.StartCmd.RunE = runStartCmd - surveyCmd.RootCmd.AddCommand(surveyCmd.StartCmd) - surveyCmd.RootCmd.AddCommand(surveyCmd.SubmitCmd) - surveyCmd.RootCmd.AddCommand(surveyCmd.StatusCmd) - surveyCmd.RootCmd.AddCommand(surveyCmd.ListTasksCmd) - surveyCmd.RootCmd.AddCommand(surveyCmd.ListInterfacesCmd) - surveyCmd.RootCmd.AddCommand(surveyCmd.IssuesCmd) + survey.StartCmd.RunE = runStartCmd + survey.RootCmd.AddCommand(survey.StartCmd) + survey.RootCmd.AddCommand(survey.SubmitCmd) + survey.RootCmd.AddCommand(survey.StatusCmd) + survey.RootCmd.AddCommand(survey.ListTasksCmd) + survey.RootCmd.AddCommand(survey.ListInterfacesCmd) + survey.RootCmd.AddCommand(survey.IssuesCmd) // Issue related commands - surveyCmd.IssuesCmd.AddCommand(issuesCmd.ListCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.CreateCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.UpdateCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.DeleteCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.CloseCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.CommentCmd) - surveyCmd.IssuesCmd.AddCommand(issuesCmd.CommentsCmd) + survey.IssuesCmd.AddCommand(issues.ListCmd) + survey.IssuesCmd.AddCommand(issues.CreateCmd) + survey.IssuesCmd.AddCommand(issues.UpdateCmd) + survey.IssuesCmd.AddCommand(issues.DeleteCmd) + survey.IssuesCmd.AddCommand(issues.CloseCmd) + survey.IssuesCmd.AddCommand(issues.CommentCmd) + survey.IssuesCmd.AddCommand(issues.CommentsCmd) // Issue commands for the REPL interface - issuesCmd.RootCmd.AddCommand(issuesCmd.GetCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.ListCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CloseCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CreateCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.DeleteCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.UpdateCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CommentCmd) - issuesCmd.RootCmd.AddCommand(issuesCmd.CommentsCmd) - issuesCmd.RootCmd.AddCommand(surveyCmd.StatusCmd) + issues.RootCmd.AddCommand(issues.GetCmd) + issues.RootCmd.AddCommand(issues.ListCmd) + issues.RootCmd.AddCommand(issues.CloseCmd) + issues.RootCmd.AddCommand(issues.CreateCmd) + issues.RootCmd.AddCommand(issues.DeleteCmd) + issues.RootCmd.AddCommand(issues.UpdateCmd) + issues.RootCmd.AddCommand(issues.CommentCmd) + issues.RootCmd.AddCommand(issues.CommentsCmd) + issues.RootCmd.AddCommand(survey.StatusCmd) } diff --git a/cmd/survey/runner.go b/cmd/survey/runner.go index 17d4263..1c3a78c 100644 --- a/cmd/survey/runner.go +++ b/cmd/survey/runner.go @@ -7,33 +7,33 @@ import ( "math/rand" "github.com/LazyBachelor/LazyPM/cmd/survey/tasks" - surveyCmd "github.com/LazyBachelor/LazyPM/internal/commands/survey" + survey "github.com/LazyBachelor/LazyPM/internal/commands/survey" "github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/pkg/task" "github.com/spf13/cobra" ) func runStartCmd(cmd *cobra.Command, args []string) error { - app := surveyCmd.AppFromContext(cmd.Context()) + app := survey.AppFromContext(cmd.Context()) interfaces := initInterfaces() surveyTasks := initTasks(app) if cmd.Flags().Changed("interface") { - if _, ok := interfaces[surveyCmd.InterfaceType]; !ok { + if _, ok := interfaces[survey.InterfaceType]; !ok { return fmt.Errorf("invalid interface, valid are %v", task.ListInterfaces()) } interfaces = map[string]task.Interface{ - surveyCmd.InterfaceType: interfaces[surveyCmd.InterfaceType], + survey.InterfaceType: interfaces[survey.InterfaceType], } } if cmd.Flags().Changed("task") { - if surveyTask := surveyTasks[surveyCmd.Task]; surveyTask == nil { + if surveyTask := surveyTasks[survey.Task]; surveyTask == nil { return fmt.Errorf("invalid task, valid are %v", task.ListTasks()) } surveyTasks = map[string]task.Tasker{ - surveyCmd.Task: surveyTasks[surveyCmd.Task], + survey.Task: surveyTasks[survey.Task], } } diff --git a/cmd/survey/tasks/backlogRefinement.go b/cmd/survey/tasks/backlogRefinement.go index 6fce225..be536b5 100644 --- a/cmd/survey/tasks/backlogRefinement.go +++ b/cmd/survey/tasks/backlogRefinement.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -120,7 +120,7 @@ func (t *BacklogRefinementTask) Setup(ctx context.Context) error { } func (t *BacklogRefinementTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/base.go b/cmd/survey/tasks/base.go index dafae93..52065ac 100644 --- a/cmd/survey/tasks/base.go +++ b/cmd/survey/tasks/base.go @@ -3,8 +3,8 @@ package tasks import ( "context" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/repl" "github.com/LazyBachelor/LazyPM/pkg/task" "github.com/LazyBachelor/LazyPM/pkg/tui" @@ -12,7 +12,7 @@ import ( "github.com/charmbracelet/huh" ) -type App = service.App +type App = app.App type Config = models.Config type ValidationFeedback = models.ValidationFeedback @@ -113,7 +113,7 @@ func TUIQuestion(interfaceType InterfaceType, fields ...huh.Field) *huh.Group { // FetchIssues retrives all issues from the app and returns those that are relevant for validation, // excluding the setup issue. It also updates the setup issue with the latest data from the app. -func FetchIssues(ctx context.Context, app *service.App, setupIssue *models.Issue) ([]*models.Issue, error) { +func FetchIssues(ctx context.Context, app *app.App, setupIssue *models.Issue) ([]*models.Issue, error) { issues, err := app.Issues.SearchIssues(ctx, "", models.IssueFilter{}) if err != nil { return nil, err diff --git a/cmd/survey/tasks/codingTask.go b/cmd/survey/tasks/codingTask.go index d086612..24165f0 100644 --- a/cmd/survey/tasks/codingTask.go +++ b/cmd/survey/tasks/codingTask.go @@ -4,7 +4,7 @@ import ( "context" "os" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -80,7 +80,7 @@ func (t *CodingTask) Setup(ctx context.Context) error { } func (t *CodingTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() expect.Assert(true, "This task is always valid") return expect.Complete() } diff --git a/cmd/survey/tasks/createIssue.go b/cmd/survey/tasks/createIssue.go index 3907997..b10c32c 100644 --- a/cmd/survey/tasks/createIssue.go +++ b/cmd/survey/tasks/createIssue.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" ) const description = `You are tasked with creating a new issue in the project management system. @@ -56,7 +56,7 @@ func (t *CreateIssueTask) Setup(ctx context.Context) error { } func (t *CreateIssueTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() issues, err := FetchIssues(ctx, t.app, t.setupIssue) if err != nil { diff --git a/cmd/survey/tasks/dependencyManagement.go b/cmd/survey/tasks/dependencyManagement.go index 6816f27..0c52d5f 100644 --- a/cmd/survey/tasks/dependencyManagement.go +++ b/cmd/survey/tasks/dependencyManagement.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -23,7 +23,7 @@ Resolving dependencies in the right order is critical for efficient team workflo type DependencyManagementTask struct { done bool app *App - setupIssue *models.Issue + setupIssue *Issue } func NewDependencyManagementTask(app *App) *DependencyManagementTask { @@ -62,35 +62,35 @@ func (t *DependencyManagementTask) Setup(ctx context.Context) error { } depIssues := []*models.Issue{ - models.NewIssueBuilder(). + NewIssueBuilder(). WithTitle("Setup database connection"). WithDescription("Configure database connection pool."). WithPriority(1). WithStatus(models.StatusOpen). WithIssueType(models.TypeTask). Build(), - models.NewIssueBuilder(). + NewIssueBuilder(). WithTitle("Define API contract"). WithDescription("Create OpenAPI spec."). WithPriority(1). WithStatus(models.StatusOpen). WithIssueType(models.TypeTask). Build(), - models.NewIssueBuilder(). + NewIssueBuilder(). WithTitle("Implement user repository"). WithDescription("Implement data access layer for user management."). WithPriority(2). WithStatus(models.StatusBlocked). WithIssueType(models.TypeTask). Build(), - models.NewIssueBuilder(). + NewIssueBuilder(). WithTitle("Create user endpoints"). WithDescription("REST API for users."). WithPriority(2). WithStatus(models.StatusBlocked). WithIssueType(models.TypeTask). Build(), - models.NewIssueBuilder(). + NewIssueBuilder(). WithTitle("Build user profile UI"). WithDescription("Frontend user profile page."). WithPriority(3). @@ -103,7 +103,7 @@ func (t *DependencyManagementTask) Setup(ctx context.Context) error { return err } - t.setupIssue = models.NewBaseIssue(). + t.setupIssue = NewIssueBuilder(). WithTitle("Dependency Management"). WithDescription(dependencyManagementDescription). Build() @@ -112,7 +112,7 @@ func (t *DependencyManagementTask) Setup(ctx context.Context) error { } func (t *DependencyManagementTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/gitTask.go b/cmd/survey/tasks/gitTask.go index a474169..12f39c2 100644 --- a/cmd/survey/tasks/gitTask.go +++ b/cmd/survey/tasks/gitTask.go @@ -6,8 +6,7 @@ import ( "os" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" "github.com/go-git/go-git/v6" ) @@ -26,11 +25,10 @@ Your task: The repository has been initialized in ./task/.git/ for you to work with.` type GitTask struct { - setupIssue *models.Issue - repo *git.Repository + app *App done bool - - app *service.App + repo *git.Repository + setupIssue *Issue } func NewGitTask(app *App) *GitTask { @@ -72,7 +70,7 @@ func (t *GitTask) Setup(ctx context.Context) error { os.WriteFile("./task/README.md", []byte("This is a Git task. Please perform a Git operation here."), 0o644) - t.setupIssue = models.NewIssueBuilder(). + t.setupIssue = NewIssueBuilder(). WithTitle("Git Task Setup Issue"). WithDescription(gitTaskDescription). WithIssueType(models.TypeTask). @@ -86,7 +84,7 @@ func (t *GitTask) Setup(ctx context.Context) error { } func (t *GitTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/issueTriage.go b/cmd/survey/tasks/issueTriage.go index f6cdce4..a2c64ce 100644 --- a/cmd/survey/tasks/issueTriage.go +++ b/cmd/survey/tasks/issueTriage.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -113,7 +113,7 @@ func (t *IssueTriageTask) Setup(ctx context.Context) error { } func (t *IssueTriageTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/milestoneTracking.go b/cmd/survey/tasks/milestoneTracking.go index 483ef8b..f6ce7fd 100644 --- a/cmd/survey/tasks/milestoneTracking.go +++ b/cmd/survey/tasks/milestoneTracking.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -113,7 +113,7 @@ func (t *MilestoneTrackingTask) Setup(ctx context.Context) error { } func (t *MilestoneTrackingTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/priorityManagement.go b/cmd/survey/tasks/priorityManagement.go index 2313b54..a5b2b5c 100644 --- a/cmd/survey/tasks/priorityManagement.go +++ b/cmd/survey/tasks/priorityManagement.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -114,7 +114,7 @@ func (t *PriorityManagementTask) Setup(ctx context.Context) error { } func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/reportGeneration.go b/cmd/survey/tasks/reportGeneration.go index d5d4c43..2b3e735 100644 --- a/cmd/survey/tasks/reportGeneration.go +++ b/cmd/survey/tasks/reportGeneration.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -113,7 +113,7 @@ func (t *ReportGenerationTask) Setup(ctx context.Context) error { } func (t *ReportGenerationTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/sprintPlanning.go b/cmd/survey/tasks/sprintPlanning.go index 409521d..572fa41 100644 --- a/cmd/survey/tasks/sprintPlanning.go +++ b/cmd/survey/tasks/sprintPlanning.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -114,7 +114,7 @@ func (t *SprintPlanningTask) Setup(ctx context.Context) error { } func (t *SprintPlanningTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() diff --git a/cmd/survey/tasks/stakeholderUpdate.go b/cmd/survey/tasks/stakeholderUpdate.go index 1507cbf..de34665 100644 --- a/cmd/survey/tasks/stakeholderUpdate.go +++ b/cmd/survey/tasks/stakeholderUpdate.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -108,7 +108,7 @@ func (t *StakeholderUpdateTask) Setup(ctx context.Context) error { } func (t *StakeholderUpdateTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/cmd/survey/tasks/teamCapacity.go b/cmd/survey/tasks/teamCapacity.go index a95c21e..3f3fb03 100644 --- a/cmd/survey/tasks/teamCapacity.go +++ b/cmd/survey/tasks/teamCapacity.go @@ -4,7 +4,7 @@ import ( "context" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/check" "github.com/charmbracelet/huh" ) @@ -120,7 +120,7 @@ func (t *TeamCapacityTask) Setup(ctx context.Context) error { } func (t *TeamCapacityTask) Validate(ctx context.Context) ValidationFeedback { - expect := utils.NewExpector() + expect := check.NewExpector() return expect.Complete() } diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..bb7a4cb --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,66 @@ +package app + +import ( + "context" + "time" + + "github.com/LazyBachelor/LazyPM/internal/models" + "github.com/LazyBachelor/LazyPM/internal/storage" + "github.com/steveyegge/beads" +) + +type App = models.App +type Config = models.Config + +func New(ctx context.Context, config Config, opts ...Option) (*App, func(), error) { + b := defaultBuilder(ctx, config) + + for _, opt := range opts { + if err := opt(b); err != nil { + return nil, nil, err + } + } + + if !config.AutoInit { + if err := b.initializer.Init(config.BeadsDBPath); err != nil { + return nil, nil, err + } + } + + if b.issueService == nil { + sqliteStore, err := beads.NewSQLiteStorage(b.ctx, config.BeadsDBPath) + if err != nil { + return nil, nil, err + } + b.lifecycle.Add(func() { sqliteStore.Close() }) + + b.issueService, err = storage.NewBeadsIssueStorage(b.ctx, sqliteStore, config.IssuePrefix) + if err != nil { + return nil, nil, err + } + } + + if b.statsService == nil { + statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{ + ID: 0, + StartTime: time.Now(), + }) + + jsonStatService, err := NewStatisticsService(statStore) + if err != nil { + return nil, nil, err + } + + b.statsService = jsonStatService + } + + app := &App{ + Config: config, + Logger: b.logger, + + Issues: b.issueService, + Stats: b.statsService, + } + + return app, b.lifecycle.Close, nil +} diff --git a/internal/app/initializer.go b/internal/app/initializer.go new file mode 100644 index 0000000..3c52b3d --- /dev/null +++ b/internal/app/initializer.go @@ -0,0 +1,41 @@ +package app + +import ( + "fmt" + "os" + + "github.com/charmbracelet/huh" +) + +type Initializer interface { + Init(path string) error +} + +type InteractiveInitializer struct{} + +func (i InteractiveInitializer) Init(path string) error { + _, err := os.Stat(path) + if !os.IsNotExist(err) { + return nil + } + + var initialize bool + + err = huh.NewForm( + huh.NewGroup( + huh.NewConfirm(). + Title("PM is not initialized in this directory!"). + Description("Do you want to initialize it here?"). + Value(&initialize), + )).WithTheme(huh.ThemeBase16()).WithAccessible(true).Run() + + if err != nil { + return err + } + + if !initialize { + return fmt.Errorf("project not initialized") + } + + return nil +} diff --git a/internal/app/lifecycle.go b/internal/app/lifecycle.go new file mode 100644 index 0000000..27a09ae --- /dev/null +++ b/internal/app/lifecycle.go @@ -0,0 +1,21 @@ +package app + +type Lifecycle struct { + cleanups []func() +} + +func NewLifecycle() *Lifecycle { + return &Lifecycle{ + cleanups: make([]func(), 0), + } +} + +func (l *Lifecycle) Add(fn func()) { + l.cleanups = append(l.cleanups, fn) +} + +func (l *Lifecycle) Close() { + for i := len(l.cleanups) - 1; i >= 0; i-- { + l.cleanups[i]() + } +} diff --git a/internal/app/options.go b/internal/app/options.go new file mode 100644 index 0000000..840c865 --- /dev/null +++ b/internal/app/options.go @@ -0,0 +1,68 @@ +package app + +import ( + "context" + "log/slog" + "os" + + "github.com/LazyBachelor/LazyPM/internal/models" +) + +type Option func(*AppBuilder) error + +type AppBuilder struct { + config Config + ctx context.Context + + logger *slog.Logger + lifecycle *Lifecycle + initializer Initializer + + issueService models.IssueService + statsService models.StatsService +} + +func defaultBuilder(ctx context.Context, config Config) *AppBuilder { + return &AppBuilder{ + ctx: ctx, + config: config, + lifecycle: NewLifecycle(), + initializer: &InteractiveInitializer{}, + logger: slog.New(slog.NewJSONHandler(os.Stdout, nil)), + } +} + +func WithLogger(logger *slog.Logger) Option { + return func(b *AppBuilder) error { + b.logger = logger + return nil + } +} + +func WithInitializer(initializer Initializer) Option { + return func(b *AppBuilder) error { + b.initializer = initializer + return nil + } +} + +func WithLifecycle(l *Lifecycle) Option { + return func(b *AppBuilder) error { + b.lifecycle = l + return nil + } +} + +func WithIssueService(svc models.IssueService) Option { + return func(b *AppBuilder) error { + b.issueService = svc + return nil + } +} + +func WithStatsService(svc models.StatsService) Option { + return func(b *AppBuilder) error { + b.statsService = svc + return nil + } +} diff --git a/internal/service/statistics.go b/internal/app/statistics.go similarity index 98% rename from internal/service/statistics.go rename to internal/app/statistics.go index b24b81a..7e00386 100644 --- a/internal/service/statistics.go +++ b/internal/app/statistics.go @@ -1,4 +1,4 @@ -package service +package app import ( "context" diff --git a/internal/commands/issues/close.go b/internal/commands/issues/close.go index 3c3dc80..3c89b10 100644 --- a/internal/commands/issues/close.go +++ b/internal/commands/issues/close.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "fmt" diff --git a/internal/commands/issues/comment.go b/internal/commands/issues/comment.go index decef9c..0cdbdc5 100644 --- a/internal/commands/issues/comment.go +++ b/internal/commands/issues/comment.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "fmt" @@ -70,7 +70,6 @@ func runCommentCmd(cmd *cobra.Command, args []string) error { return nil } - func defaultCommentAuthor() string { if u, err := user.Current(); err == nil && u.Username != "" { return u.Username diff --git a/internal/commands/issues/comments_list.go b/internal/commands/issues/comments_list.go index 897ebfb..762efef 100644 --- a/internal/commands/issues/comments_list.go +++ b/internal/commands/issues/comments_list.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "fmt" diff --git a/internal/commands/issues/completion.go b/internal/commands/issues/completion.go index 04c54a8..a363235 100644 --- a/internal/commands/issues/completion.go +++ b/internal/commands/issues/completion.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "context" diff --git a/internal/commands/issues/create.go b/internal/commands/issues/create.go index 97d0262..c69aa68 100644 --- a/internal/commands/issues/create.go +++ b/internal/commands/issues/create.go @@ -1,11 +1,11 @@ -package issuesCmd +package issues import ( "fmt" "strings" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/shellcomp" "github.com/charmbracelet/huh" "github.com/spf13/cobra" @@ -112,7 +112,7 @@ func init() { CreateCmd.Flags().StringVarP(&createFlags.issueType, "type", "t", "task", "Issue type(bug, feature, task)") CreateCmd.Flags().IntVarP(&createFlags.priority, "priority", "p", 0, "Issue priority(0-4)") - CreateCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions)) - CreateCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions)) - CreateCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange)) + CreateCmd.RegisterFlagCompletionFunc("type", shellcomp.CompletionFunc(typeOptions)) + CreateCmd.RegisterFlagCompletionFunc("status", shellcomp.CompletionFunc(statusOptions)) + CreateCmd.RegisterFlagCompletionFunc("priority", shellcomp.CompletionFunc(priorityRange)) } diff --git a/internal/commands/issues/delete.go b/internal/commands/issues/delete.go index 8a4ae2e..019b3f9 100644 --- a/internal/commands/issues/delete.go +++ b/internal/commands/issues/delete.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "context" diff --git a/internal/commands/issues/list.go b/internal/commands/issues/list.go index 14c226c..bd0bd63 100644 --- a/internal/commands/issues/list.go +++ b/internal/commands/issues/list.go @@ -1,10 +1,10 @@ -package issuesCmd +package issues import ( "strings" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/shellcomp" "github.com/spf13/cobra" ) @@ -79,7 +79,7 @@ func init() { ListCmd.Flags().IntVarP(&listFlags.limit, "limit", "l", 25, "Limit the number of issues returned") - ListCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions)) - ListCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions)) - ListCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange)) + ListCmd.RegisterFlagCompletionFunc("status", shellcomp.CompletionFunc(statusOptions)) + ListCmd.RegisterFlagCompletionFunc("type", shellcomp.CompletionFunc(typeOptions)) + ListCmd.RegisterFlagCompletionFunc("priority", shellcomp.CompletionFunc(priorityRange)) } diff --git a/internal/commands/issues/read.go b/internal/commands/issues/read.go index c070fcf..81cd4f4 100644 --- a/internal/commands/issues/read.go +++ b/internal/commands/issues/read.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "github.com/LazyBachelor/LazyPM/internal/models" diff --git a/internal/commands/issues/root.go b/internal/commands/issues/root.go index e08d51f..25ad934 100644 --- a/internal/commands/issues/root.go +++ b/internal/commands/issues/root.go @@ -1,4 +1,4 @@ -package issuesCmd +package issues import ( "bytes" diff --git a/internal/commands/issues/update.go b/internal/commands/issues/update.go index 05dceb0..24e570f 100644 --- a/internal/commands/issues/update.go +++ b/internal/commands/issues/update.go @@ -1,10 +1,10 @@ -package issuesCmd +package issues import ( "fmt" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/shellcomp" "github.com/spf13/cobra" ) @@ -62,9 +62,9 @@ func init() { UpdateCmd.Flags().StringVarP(&updateFlags.issueType, "type", "t", "", "New issue type(bug, feature, task)") UpdateCmd.Flags().IntVarP(&updateFlags.priority, "priority", "p", 0, "New issue priority(0-5)") - UpdateCmd.RegisterFlagCompletionFunc("type", utils.CompletionFunc(typeOptions)) - UpdateCmd.RegisterFlagCompletionFunc("status", utils.CompletionFunc(statusOptions)) - UpdateCmd.RegisterFlagCompletionFunc("priority", utils.CompletionFunc(priorityRange)) + UpdateCmd.RegisterFlagCompletionFunc("type", shellcomp.CompletionFunc(typeOptions)) + UpdateCmd.RegisterFlagCompletionFunc("status", shellcomp.CompletionFunc(statusOptions)) + UpdateCmd.RegisterFlagCompletionFunc("priority", shellcomp.CompletionFunc(priorityRange)) } func getUpdateValues(cmd *cobra.Command) (map[string]interface{}, error) { diff --git a/internal/commands/survey/issues.go b/internal/commands/survey/issues.go index d30ddc5..e8b2694 100644 --- a/internal/commands/survey/issues.go +++ b/internal/commands/survey/issues.go @@ -1,4 +1,4 @@ -package surveyCmd +package survey import "github.com/spf13/cobra" diff --git a/internal/commands/survey/list.go b/internal/commands/survey/list.go index b0e6e0d..fe66eed 100644 --- a/internal/commands/survey/list.go +++ b/internal/commands/survey/list.go @@ -1,4 +1,4 @@ -package surveyCmd +package survey import ( "github.com/LazyBachelor/LazyPM/pkg/task" diff --git a/internal/commands/survey/root.go b/internal/commands/survey/root.go index ebf6edf..41a4d21 100644 --- a/internal/commands/survey/root.go +++ b/internal/commands/survey/root.go @@ -1,4 +1,4 @@ -package surveyCmd +package survey import ( "context" diff --git a/internal/commands/survey/start.go b/internal/commands/survey/start.go index 62ebba8..bd2676b 100644 --- a/internal/commands/survey/start.go +++ b/internal/commands/survey/start.go @@ -1,7 +1,7 @@ -package surveyCmd +package survey import ( - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/shellcomp" "github.com/LazyBachelor/LazyPM/pkg/task" "github.com/spf13/cobra" ) @@ -20,6 +20,6 @@ var StartCmd = &cobra.Command{ func init() { StartCmd.Flags().StringVarP(&Task, "task", "t", "", "Specify task.") StartCmd.Flags().StringVarP(&InterfaceType, "interface", "i", "", "Specify interface.") - StartCmd.RegisterFlagCompletionFunc("task", utils.CompletionFunc(task.ListTasks())) - StartCmd.RegisterFlagCompletionFunc("interface", utils.CompletionFunc(task.ListInterfaces())) + StartCmd.RegisterFlagCompletionFunc("task", shellcomp.CompletionFunc(task.ListTasks())) + StartCmd.RegisterFlagCompletionFunc("interface", shellcomp.CompletionFunc(task.ListInterfaces())) } diff --git a/internal/commands/survey/status.go b/internal/commands/survey/status.go index 8983bff..2168d18 100644 --- a/internal/commands/survey/status.go +++ b/internal/commands/survey/status.go @@ -1,4 +1,4 @@ -package surveyCmd +package survey import ( "github.com/LazyBachelor/LazyPM/internal/commands/issues" @@ -14,7 +14,7 @@ var StatusCmd = &cobra.Command{ } func runStatusCmd(cmd *cobra.Command, args []string) error { - app := issuesCmd.AppFromContext(cmd.Context()) + app := issues.AppFromContext(cmd.Context()) if app == nil || app.CurrentFeedback == nil { cmd.Println("No validation status available.") return nil diff --git a/internal/commands/survey/submit.go b/internal/commands/survey/submit.go index 9766e6c..205d93e 100644 --- a/internal/commands/survey/submit.go +++ b/internal/commands/survey/submit.go @@ -1,4 +1,4 @@ -package surveyCmd +package survey import "github.com/spf13/cobra" diff --git a/internal/models/app.go b/internal/models/app.go index e43a674..1eb580c 100644 --- a/internal/models/app.go +++ b/internal/models/app.go @@ -14,8 +14,8 @@ type App struct { Logger *slog.Logger - Tasks *map[string]Tasker - Interfaces *map[string]Interface + Tasks map[string]Tasker + Interfaces map[string]Interface CurrentFeedback *ValidationFeedback } diff --git a/internal/service/app.go b/internal/service/app.go deleted file mode 100644 index 0ddcef6..0000000 --- a/internal/service/app.go +++ /dev/null @@ -1,87 +0,0 @@ -package service - -import ( - "context" - "fmt" - "log/slog" - "os" - "time" - - "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/storage" - "github.com/charmbracelet/huh" - "github.com/steveyegge/beads" -) - -type App = models.App -type Config = models.Config - -func NewApp(ctx context.Context, config Config) (*App, func(), error) { - var cleanupFuncs []func() - - if !config.AutoInit { - if !initialized(config.BeadsDBPath) { - fmt.Println("PM is not initialized") - os.Exit(0) - } - } - - store, err := beads.NewSQLiteStorage(ctx, config.BeadsDBPath) - if err != nil { - return nil, nil, err - } - cleanupFuncs = append(cleanupFuncs, func() { store.Close() }) - - beadsSvc, err := storage.NewBeadsIssueStorage(ctx, store, config.IssuePrefix) - if err != nil { - return nil, nil, err - } - cleanupFuncs = append(cleanupFuncs, func() { beadsSvc.Close() }) - - statStore := storage.NewJsonStorage(config.StatisticsStoragePath, &models.Statistics{ - ID: 0, - StartTime: time.Now(), - }) - - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - - statSvc, err := NewStatisticsService(statStore) - if err != nil { - return nil, nil, err - } - - return &App{ - Issues: beadsSvc, - Stats: statSvc, - Config: config, - Logger: logger, - }, func() { runCleanup(cleanupFuncs) }, nil - -} - -func runCleanup(funcs []func()) { - for _, fn := range funcs { - fn() - } -} - -func initialized(beadsPath string) bool { - _, err := os.Stat(beadsPath) - - if os.IsNotExist(err) { - var initialize bool - - huh.NewForm( - huh.NewGroup( - huh.NewConfirm().Title("PM is not initialized in this directory!"). - Description("Do you want to initialize it here?"). - Value(&initialize), - ), - ).WithTheme(huh.ThemeBase16()).WithAccessible(true).Run() - - if !initialize { - return false - } - } - return true -} diff --git a/internal/storage/storage.go b/internal/storage/json.go similarity index 100% rename from internal/storage/storage.go rename to internal/storage/json.go diff --git a/internal/utils/browser.go b/internal/utils/browser/browser.go similarity index 89% rename from internal/utils/browser.go rename to internal/utils/browser/browser.go index 1c4bac7..d8daa8c 100644 --- a/internal/utils/browser.go +++ b/internal/utils/browser/browser.go @@ -1,4 +1,4 @@ -package utils +package browser import ( "fmt" @@ -7,7 +7,7 @@ import ( ) // Taken and modified from https://gist.github.com/hyg/9c4afcd91fe24316cbf0 -func OpenBrowser(url string) error { +func Open(url string) error { var err error switch runtime.GOOS { diff --git a/internal/utils/expect.go b/internal/utils/check/expect.go similarity index 99% rename from internal/utils/expect.go rename to internal/utils/check/expect.go index ea36de5..cfa40a2 100644 --- a/internal/utils/expect.go +++ b/internal/utils/check/expect.go @@ -1,4 +1,4 @@ -package utils +package check import ( "fmt" @@ -7,8 +7,8 @@ import ( "github.com/LazyBachelor/LazyPM/internal/models" ) -type ValidationFeedback = models.ValidationFeedback type Check = models.Check +type ValidationFeedback = models.ValidationFeedback func NewCheck(message string, valid bool) Check { return Check{ diff --git a/internal/utils/completion.go b/internal/utils/shellcomp/completion.go similarity index 95% rename from internal/utils/completion.go rename to internal/utils/shellcomp/completion.go index 662660e..462d728 100644 --- a/internal/utils/completion.go +++ b/internal/utils/shellcomp/completion.go @@ -1,4 +1,4 @@ -package utils +package shellcomp import "github.com/spf13/cobra" diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index b64da96..1ecfb79 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -4,14 +4,14 @@ package cli import ( "context" - issuesCmd "github.com/LazyBachelor/LazyPM/internal/commands/issues" + "github.com/LazyBachelor/LazyPM/internal/app" + issues "github.com/LazyBachelor/LazyPM/internal/commands/issues" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/charmbracelet/fang" "github.com/spf13/cobra" ) -// Config is an alias for service.Config, used to configure the CLI. +// Config is an alias for app.Config, used to configure the CLI. type Config = models.Config type CLI struct { @@ -26,14 +26,14 @@ func New(rootCmd *cobra.Command) *CLI { // Run initializes the services and executes the CLI commands. func (c *CLI) Run(ctx context.Context, config Config) error { - app, cleanup, err := service.NewApp(ctx, config) + app, cleanup, err := app.New(ctx, config) if err != nil { return err } defer cleanup() - issuesCmd.SetApp(app) + issues.SetApp(app) if err := fang.Execute(ctx, c.RootCmd, fang.WithColorSchemeFunc(fang.AnsiColorScheme)); err != nil { @@ -45,16 +45,16 @@ func (c *CLI) Run(ctx context.Context, config Config) error { // RunWithArgs initializes the services and executes the CLI commands with the provided arguments. func (c *CLI) RunWithArgs(ctx context.Context, config Config, args []string) error { - app, cleanup, err := service.NewApp(ctx, config) + app, cleanup, err := app.New(ctx, config) if err != nil { return err } defer cleanup() - issuesCmd.SetApp(app) + issues.SetApp(app) - if err := issuesCmd.ExecuteArgs(args); err != nil { + if err := issues.ExecuteArgs(args); err != nil { return err } diff --git a/pkg/repl/executor.go b/pkg/repl/executor.go index 037528f..ff254f4 100644 --- a/pkg/repl/executor.go +++ b/pkg/repl/executor.go @@ -44,6 +44,6 @@ func executePMCommand(input string) (string, error) { return "", nil } - output, err := issuesCmd.ExecuteArgsString(parts) + output, err := issues.ExecuteArgsString(parts) return output, err } diff --git a/pkg/repl/repl.go b/pkg/repl/repl.go index bf6d460..ce3c52b 100644 --- a/pkg/repl/repl.go +++ b/pkg/repl/repl.go @@ -7,9 +7,9 @@ import ( "os" "strings" - issuesCmd "github.com/LazyBachelor/LazyPM/internal/commands/issues" + "github.com/LazyBachelor/LazyPM/internal/app" + issues "github.com/LazyBachelor/LazyPM/internal/commands/issues" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/internal/style" "github.com/LazyBachelor/LazyPM/pkg/cli" "github.com/LazyBachelor/LazyPM/pkg/task" @@ -54,14 +54,14 @@ func (r *REPL) Run(ctx context.Context, config cli.Config) error { defer term.Restore(int(os.Stdin.Fd()), oldState) // Initialize services for beads, config and stats. - app, cleanup, err := service.NewApp(ctx, config) + app, cleanup, err := app.New(ctx, config) if err != nil { return fmt.Errorf("failed to initialize services: %w", err) } defer cleanup() // Make sure to set app, to ensure they are available. - issuesCmd.SetApp(app) + issues.SetApp(app) // Store app reference for updating feedback r.app = app diff --git a/pkg/repl/suggestions.go b/pkg/repl/suggestions.go index 48fd494..360791d 100644 --- a/pkg/repl/suggestions.go +++ b/pkg/repl/suggestions.go @@ -162,7 +162,7 @@ func issueIDSuggestions(partial string, hasCommand bool) []prompt.Suggest { return nil } - issues, _ := issuesCmd.GetIssueCompletions(context.Background(), partial) + issues, _ := issues.GetIssueCompletions(context.Background(), partial) var suggestions []prompt.Suggest for _, issue := range issues { diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go index cfed482..6227f27 100644 --- a/pkg/tui/tui.go +++ b/pkg/tui/tui.go @@ -3,8 +3,8 @@ package tui import ( "context" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/tui/views" tea "github.com/charmbracelet/bubbletea" ) @@ -22,7 +22,7 @@ func New() *Tui { } func (t *Tui) Run(ctx context.Context, config Config) error { - app, cleanup, err := service.NewApp(ctx, config) + app, cleanup, err := app.New(ctx, config) if err != nil { return err } diff --git a/pkg/tui/views/dashboard/issue_list.go b/pkg/tui/views/dashboard/issue_list.go index 500fd78..c2eb4cb 100644 --- a/pkg/tui/views/dashboard/issue_list.go +++ b/pkg/tui/views/dashboard/issue_list.go @@ -6,8 +6,8 @@ import ( "io" "sort" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/tui/styles" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" @@ -17,7 +17,7 @@ import ( type IssueList struct { list list.Model - app *service.App + app *app.App width int height int } @@ -77,7 +77,7 @@ func renderHeaders(cols []TableColumn) string { return lipgloss.JoinHorizontal(lipgloss.Left, parts...) } -func NewIssueList(app *service.App, width, height int) IssueList { +func NewIssueList(app *app.App, width, height int) IssueList { issues, err := app.Issues.SearchIssues(context.Background(), "", models.IssueFilter{}) if err != nil { return IssueList{} @@ -111,7 +111,7 @@ func NewIssueList(app *service.App, width, height int) IssueList { } } -func NewIssueListFromIssues(app *service.App, issues []*models.Issue, width, height int) IssueList { +func NewIssueListFromIssues(app *app.App, issues []*models.Issue, width, height int) IssueList { // for making an IssueList from a pre-existing list of issues. listIssues := make([]list.Item, len(issues)) for i, issue := range issues { diff --git a/pkg/tui/views/dashboard/model.go b/pkg/tui/views/dashboard/model.go index cfe2023..dde0979 100644 --- a/pkg/tui/views/dashboard/model.go +++ b/pkg/tui/views/dashboard/model.go @@ -3,8 +3,8 @@ package dashboard import ( "context" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" @@ -21,7 +21,7 @@ type Model struct { closedIssueList IssueList helpBar HelpBar keyMap DashboardKeyMap - app *service.App + app *app.App width int height int focusedWindow int // 0 = main (display issues), 1 = closed issues @@ -46,14 +46,14 @@ type Model struct { choosingPriority bool // true while choosing a priority priorityIssueID string choosingType bool // true while choosing a type - typeIssueID string - feedbackChan chan models.ValidationFeedback - quitChan chan bool - currentFeedback models.ValidationFeedback - showComplete bool + typeIssueID string + feedbackChan chan models.ValidationFeedback + quitChan chan bool + currentFeedback models.ValidationFeedback + showComplete bool } -func NewDashboard(app *service.App, feedbackChan chan models.ValidationFeedback, quitChan chan bool) *Model { +func NewDashboard(app *app.App, feedbackChan chan models.ValidationFeedback, quitChan chan bool) *Model { m := &Model{ header: NewHeader("Project Manager Dashboard"), keyMap: defaultDashboardKeyMap, diff --git a/pkg/tui/views/dashboard/operations.go b/pkg/tui/views/dashboard/operations.go index c820244..8d53ee9 100644 --- a/pkg/tui/views/dashboard/operations.go +++ b/pkg/tui/views/dashboard/operations.go @@ -3,8 +3,8 @@ package dashboard import ( "context" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" ) @@ -49,7 +49,7 @@ type issueDeletedMsg struct { PreviousIndex int } -func updateIssueTitleCmd(app *service.App, issueID, newTitle string) tea.Cmd { +func updateIssueTitleCmd(app *app.App, issueID, newTitle string) tea.Cmd { return func() tea.Msg { updates := map[string]interface{}{"title": newTitle} err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") @@ -57,7 +57,7 @@ func updateIssueTitleCmd(app *service.App, issueID, newTitle string) tea.Cmd { } } -func updateIssueDescriptionCmd(app *service.App, issueID, newDescription string) tea.Cmd { +func updateIssueDescriptionCmd(app *app.App, issueID, newDescription string) tea.Cmd { return func() tea.Msg { updates := map[string]interface{}{"description": newDescription} err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") @@ -65,7 +65,7 @@ func updateIssueDescriptionCmd(app *service.App, issueID, newDescription string) } } -func updateIssueStatusCmd(app *service.App, issueID, status string) tea.Cmd { +func updateIssueStatusCmd(app *app.App, issueID, status string) tea.Cmd { return func() tea.Msg { updates := map[string]interface{}{"status": status} err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") @@ -73,7 +73,7 @@ func updateIssueStatusCmd(app *service.App, issueID, status string) tea.Cmd { } } -func updateIssuePriorityCmd(app *service.App, issueID string, priority int) tea.Cmd { +func updateIssuePriorityCmd(app *app.App, issueID string, priority int) tea.Cmd { return func() tea.Msg { updates := map[string]interface{}{"priority": priority} err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") @@ -81,7 +81,7 @@ func updateIssuePriorityCmd(app *service.App, issueID string, priority int) tea. } } -func updateIssueTypeCmd(app *service.App, issueID string, issueType models.IssueType) tea.Cmd { +func updateIssueTypeCmd(app *app.App, issueID string, issueType models.IssueType) tea.Cmd { return func() tea.Msg { updates := map[string]interface{}{"issue_type": string(issueType)} err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") @@ -89,7 +89,7 @@ func updateIssueTypeCmd(app *service.App, issueID string, issueType models.Issue } } -func createIssueCmd(app *service.App, title string) tea.Cmd { +func createIssueCmd(app *app.App, title string) tea.Cmd { return func() tea.Msg { issue := &models.Issue{ Title: title, @@ -102,7 +102,7 @@ func createIssueCmd(app *service.App, title string) tea.Cmd { } } -func deleteIssueCmd(app *service.App, issueID string, currentIndex int) tea.Cmd { +func deleteIssueCmd(app *app.App, issueID string, currentIndex int) tea.Cmd { return func() tea.Msg { err := app.Issues.DeleteIssue(context.Background(), issueID) return issueDeletedMsg{IssueID: issueID, Err: err, PreviousIndex: currentIndex} diff --git a/pkg/tui/views/views.go b/pkg/tui/views/views.go index 62d51de..926a35c 100644 --- a/pkg/tui/views/views.go +++ b/pkg/tui/views/views.go @@ -1,11 +1,11 @@ package views import ( + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/tui/views/dashboard" ) -func NewDashboardView(app *service.App, feedbackChan chan models.ValidationFeedback, quitChan chan bool) *dashboard.Model { +func NewDashboardView(app *app.App, feedbackChan chan models.ValidationFeedback, quitChan chan bool) *dashboard.Model { return dashboard.NewDashboard(app, feedbackChan, quitChan) } diff --git a/pkg/web/handler/context.go b/pkg/web/handler/context.go index a7631ed..778855c 100644 --- a/pkg/web/handler/context.go +++ b/pkg/web/handler/context.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/donseba/go-htmx" ) @@ -15,7 +15,7 @@ const ( htmxKey contextKey = "htmx" ) -func AppMiddleware(app *service.App) func(http.Handler) http.Handler { +func AppMiddleware(app *app.App) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), appKey, app) @@ -33,8 +33,8 @@ func HTMXMiddleware(next http.Handler) http.Handler { }) } -func App(r *http.Request) *service.App { - return r.Context().Value(appKey).(*service.App) +func App(r *http.Request) *app.App { + return r.Context().Value(appKey).(*app.App) } func HTMX(r *http.Request) *htmx.Handler { diff --git a/pkg/web/server/server.go b/pkg/web/server/server.go index 7931c5a..aeafd86 100644 --- a/pkg/web/server/server.go +++ b/pkg/web/server/server.go @@ -5,13 +5,13 @@ import ( "net/http" "time" - "github.com/LazyBachelor/LazyPM/internal/service" + "github.com/LazyBachelor/LazyPM/internal/app" ) type Server struct { Address string Assets embed.FS - App *service.App + App *app.App } // NewServer creates and configures a new HTTP server instance. diff --git a/pkg/web/web.go b/pkg/web/web.go index e5e0fff..4f23852 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -9,9 +9,9 @@ import ( "strings" "time" + "github.com/LazyBachelor/LazyPM/internal/app" "github.com/LazyBachelor/LazyPM/internal/models" - "github.com/LazyBachelor/LazyPM/internal/service" - "github.com/LazyBachelor/LazyPM/internal/utils" + "github.com/LazyBachelor/LazyPM/internal/utils/browser" "github.com/LazyBachelor/LazyPM/pkg/web/handler" "github.com/LazyBachelor/LazyPM/pkg/web/server" ) @@ -32,7 +32,7 @@ func New() *Web { var assets embed.FS func (w *Web) Run(ctx context.Context, config Config) error { - app, cleanup, err := service.NewApp(ctx, config) + app, cleanup, err := app.New(ctx, config) if err != nil { return err } @@ -52,7 +52,7 @@ func (w *Web) Run(ctx context.Context, config Config) error { address = "http://localhost" + address } - err = utils.OpenBrowser(address) + err = browser.Open(address) if err != nil { return fmt.Errorf("failed to open browser: %w", err) }