From 183f13895b127b7e6dd0dfad509beb4a2946aab7 Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Thu, 12 Feb 2026 20:40:55 +0100 Subject: [PATCH] add task system and task model for viewing task details --- cmd/survey/tasks/runner.go | 22 ++++++++++++ cmd/survey/tasks/task.go | 45 +++++++++++++++++++++++++ cmd/survey/tasks/types.go | 29 ++++++++++++++++ cmd/survey/ui/help.go | 27 +++++++++++++++ cmd/survey/ui/style.go | 14 ++++++++ cmd/survey/ui/task.go | 69 ++++++++++++++++++++++++++++++++++++++ cmd/survey/ui/types.go | 28 ++++++++++++++++ 7 files changed, 234 insertions(+) create mode 100644 cmd/survey/tasks/runner.go create mode 100644 cmd/survey/tasks/task.go create mode 100644 cmd/survey/tasks/types.go create mode 100644 cmd/survey/ui/help.go create mode 100644 cmd/survey/ui/style.go create mode 100644 cmd/survey/ui/task.go create mode 100644 cmd/survey/ui/types.go diff --git a/cmd/survey/tasks/runner.go b/cmd/survey/tasks/runner.go new file mode 100644 index 0000000..e407efe --- /dev/null +++ b/cmd/survey/tasks/runner.go @@ -0,0 +1,22 @@ +package tasks + +import ( + "context" + + "github.com/LazyBachelor/LazyPM/internal/service" + tea "github.com/charmbracelet/bubbletea" +) + +func (t *Task) IntroduceTask() error { + _, err := tea.NewProgram(t.aboutScreen, tea.WithAltScreen()).Run() + return err +} + +func (t *Task) StartInterface(ctx context.Context, cfg service.Config) error { + return t.interfaceType.Run(ctx, cfg) +} + +func (t *Task) StartQuestionnaire() error { + _, err := tea.NewProgram(t.questionnaire, tea.WithAltScreen()).Run() + return err +} diff --git a/cmd/survey/tasks/task.go b/cmd/survey/tasks/task.go new file mode 100644 index 0000000..71b54eb --- /dev/null +++ b/cmd/survey/tasks/task.go @@ -0,0 +1,45 @@ +// task.go +package tasks + +import ( + "context" + "errors" + + "github.com/LazyBachelor/LazyPM/internal/service" + tea "github.com/charmbracelet/bubbletea" +) + +func NewTask(interfaceType Interface, aboutScreen tea.Model, questionnaire tea.Model) *Task { + return &Task{ + interfaceType: interfaceType, + aboutScreen: aboutScreen, + questionnaire: questionnaire, + } +} + +func (t *Task) SetValidateFunc(fn ValidateFunc) { + t.validateFunc = fn +} + +func (t *Task) SetDbStateFunc(fn DbStateFunc) { + t.dbStateFunc = fn +} + +func (t *Task) SetInterface(interfaceType Interface) { + t.interfaceType = interfaceType +} + +func (t *Task) Validate(ctx context.Context, svc *service.Services) (bool, error) { + if t.validateFunc == nil { + return false, errors.New("validateFunc is not set") + } + return t.validateFunc(ctx, svc) +} + +func (t *Task) MigrateToTask(ctx context.Context, svc *service.Services, task *Task) error { + t = task + if t.dbStateFunc == nil { + return errors.New("dbStateFunc is not set") + } + return t.dbStateFunc(ctx, svc) +} diff --git a/cmd/survey/tasks/types.go b/cmd/survey/tasks/types.go new file mode 100644 index 0000000..bfc9e87 --- /dev/null +++ b/cmd/survey/tasks/types.go @@ -0,0 +1,29 @@ +package tasks + +import ( + "context" + + "github.com/LazyBachelor/LazyPM/internal/service" + tea "github.com/charmbracelet/bubbletea" +) + +type Interface interface { + Run(context.Context, service.Config) error +} + +type ValidateFunc func(context.Context, *service.Services) (ok bool, err error) +type DbStateFunc func(context.Context, *service.Services) error + +type Task struct { + interfaceType Interface + aboutScreen tea.Model + questionnaire tea.Model + + validateFunc ValidateFunc + dbStateFunc DbStateFunc +} + +type TaskList struct { + Todo []*Task + Done []*Task +} diff --git a/cmd/survey/ui/help.go b/cmd/survey/ui/help.go new file mode 100644 index 0000000..15b58a6 --- /dev/null +++ b/cmd/survey/ui/help.go @@ -0,0 +1,27 @@ +package ui + +import "github.com/charmbracelet/bubbles/key" + +type TaskHelpKeys struct { + Quit key.Binding + Continue key.Binding +} + +var DefaultTaskKeys = TaskHelpKeys{ + Quit: key.NewBinding( + key.WithKeys("q", "ctrl+c"), + key.WithHelp("q", "Quit"), + ), + Continue: key.NewBinding( + key.WithKeys(" "), + key.WithHelp("space", "Continue"), + ), +} + +func (h TaskHelpKeys) ShortHelp() []key.Binding { + return []key.Binding{h.Continue, h.Quit} +} + +func (h TaskHelpKeys) FullHelp() [][]key.Binding { + return [][]key.Binding{{h.Continue, h.Quit}} +} diff --git a/cmd/survey/ui/style.go b/cmd/survey/ui/style.go new file mode 100644 index 0000000..9a2b4f4 --- /dev/null +++ b/cmd/survey/ui/style.go @@ -0,0 +1,14 @@ +package ui + +import ( + "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" +) + +func theme() *huh.Theme { + theme := huh.ThemeBase16() + + theme.Focused.Base = lipgloss.NewStyle().Align(lipgloss.Center) + + return theme +} diff --git a/cmd/survey/ui/task.go b/cmd/survey/ui/task.go new file mode 100644 index 0000000..18c4011 --- /dev/null +++ b/cmd/survey/ui/task.go @@ -0,0 +1,69 @@ +package ui + +import ( + "charm.land/lipgloss/v2" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" +) + +func NewTaskModel(details TaskDetails) TaskModel { + return TaskModel{ + TaskDetails: details, + keys: DefaultTaskKeys, + help: help.New(), + } +} + +func (m TaskModel) Init() tea.Cmd { + return nil +} + +func (t TaskModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + t.SetSize(msg.Width, msg.Height) + case tea.KeyMsg: + switch { + case key.Matches(msg, t.keys.Quit): + return t, tea.Quit + case key.Matches(msg, t.keys.Continue): + return t, tea.Quit + } + } + return t, nil +} + +func (m TaskModel) View() string { + padding := 3 + + header := lipgloss.NewStyle(). + PaddingTop(padding).Width(m.width).Align(lipgloss.Center). + Bold(true).Render(m.Title) + + headerHeight := lipgloss.Height(header) + + helpView := lipgloss.NewStyle(). + PaddingBottom(padding). + Width(m.width).Align(lipgloss.Center). + Render(m.help.View(m.keys)) + + helpHeigh := lipgloss.Height(helpView) + + details := lipgloss.NewStyle().Align(lipgloss.Center). + Width(m.width).PaddingBottom(1).Render("Time to complete:", m.TimeToComplete, "Difficulty:", m.Difficulty) + + detailsHeight := lipgloss.Height(details) + + content := lipgloss.NewStyle(). + Width(m.width).Height(m.height-headerHeight-helpHeigh-detailsHeight). + Align(lipgloss.Center, lipgloss.Center). + Render(m.Description) + + return lipgloss.JoinVertical(lipgloss.Top, header, content, details, helpView) + +} + +func (m *TaskModel) SetSize(width, height int) { + m.width, m.height = width, height +} diff --git a/cmd/survey/ui/types.go b/cmd/survey/ui/types.go new file mode 100644 index 0000000..ec382be --- /dev/null +++ b/cmd/survey/ui/types.go @@ -0,0 +1,28 @@ +package ui + +import ( + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/huh" +) + +type TaskDetails struct { + Title string + Description string + TimeToComplete string + Difficulty string +} + +type TaskModel struct { + TaskDetails + keys TaskHelpKeys + help help.Model + width, height int +} + +type Questions []*huh.Group + +type QuestionnaireModel struct { + Questions + form *huh.Form + width, height int +}