L23646QEMZPB366O52BQACSTIXR5K5ARMCTCT3IWL7JIMSFMQB4AC package mainimport ("context""fmt"db "github.com/Asfolny/protastim/internal/database"tea "github.com/charmbracelet/bubbletea""github.com/charmbracelet/bubbles/spinner")type taskModel struct {id int64config *configdata *db.Taskmodal tea.Modelerr errorloading boolediting boolspinner spinner.Model}func newTask(config *config, id int64) taskModel {m := taskModel{id: id,config: config,loading: true,spinner: config.newSpinner(),}return m}func (m taskModel) fetchTask() tea.Msg {row, err := m.config.queries.GetTask(context.Background(), m.id)if err != nil {return errMsg{err}}return row}func (m taskModel) Init() tea.Cmd {return tea.Batch(m.fetchTask, m.spinner.Tick)}func (m taskModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {switch msg := msg.(type) {case db.Task:m.data = &msgm.loading = falsereturn m, nilcase errMsg:m.err = msgm.loading = falsereturn m, tea.Quitcase spinner.TickMsg:var cmd tea.Cmdm.spinner, cmd = m.spinner.Update(msg)return m, cmdcase tea.KeyMsg:switch msg.String() {case "ctrl+n":m.modal = newTaskEdit(m.config, nil)m.editing = truereturn m, m.modal.Init()}}if m.editing {var cmd tea.Cmdm.modal, cmd = m.modal.Update(msg)return m, cmd}return m, nil}func (m taskModel) View() string {if m.loading {return fmt.Sprintf("\n%s Loading\n\n", m.spinner.View())}if m.err != nil {return fmt.Sprintf("\nFailed fetching task: %v\n\n", m.err)}if m.editing {return m.modal.View()}return fmt.Sprintf("Name: %s\nDescription:\n%s\nStatus: %s\n",m.data.Name,m.data.Description.String,m.data.Status,)}
package mainimport ("context""fmt""strconv""github.com/charmbracelet/bubbles/spinner""github.com/charmbracelet/bubbles/table"tea "github.com/charmbracelet/bubbletea""github.com/charmbracelet/lipgloss")type taskListModel struct {config *configtable table.Modelerr errorloading boolspinner spinner.Model}func newTaskList(config *config) taskListModel {m := taskListModel{config: config,loading: true,spinner: config.newSpinner(),}t := table.New(table.WithColumns([]table.Column{{Title: "ID", Width: 4},{Title: "Name", Width: 10},{Title: "Status", Width: 8},}),table.WithFocused(true),table.WithHeight(8),)ts := table.DefaultStyles()ts.Header = ts.Header.BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("240")).BorderBottom(true).Bold(false)ts.Selected = ts.Selected.Foreground(lipgloss.Color("229")).Background(lipgloss.Color("57")).Bold(false)t.SetStyles(ts)m.table = treturn m}func (m taskListModel) fetchTasks() tea.Msg {m.config.mu.RLock()defer m.config.mu.RUnlock()row, err := m.config.queries.ListTasks(context.Background())if err != nil {return errMsg{err}}tableRows := make([]table.Row, len(row), len(row))for i, e := range row {tableRows[i] = table.Row{strconv.FormatInt(e.ID, 10), e.Name, e.Status}}return rows{tableRows}}func (m taskListModel) Init() tea.Cmd {return tea.Batch(m.fetchTasks, m.spinner.Tick)}func (m taskListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {var cmd tea.Cmdswitch msg := msg.(type) {case rows:m.table.SetRows(msg.data)m.loading = falsereturn m, nilcase errMsg:m.err = msgm.loading = falsereturn m, tea.Quitcase tea.KeyMsg:switch msg.String() {case "ctrl+n":return m, changeView(newTaskEdit(m.config, nil))case "enter":id, _ := strconv.ParseInt(m.table.SelectedRow()[0], 10, 64)return m, changeView(newTask(m.config, id))}}m.table, cmd = m.table.Update(msg)return m, cmd}func (m taskListModel) View() string {if m.loading {return fmt.Sprintf("\n%s Loading\n\n", m.spinner.View())}if m.err != nil {return fmt.Sprintf("\nFailed fetching project: %v\n\n", m.err)}return m.config.styles.BaseTable.Render(m.table.View()) + "\n"}
package mainimport ("context""database/sql""fmt""strings"db "github.com/Asfolny/protastim/internal/database"tea "github.com/charmbracelet/bubbletea""github.com/charmbracelet/huh""github.com/charmbracelet/lipgloss")type taskEditModel struct {config *configform *huh.Formwidth intcreate bool}func newTaskEdit(config *config, task *db.Task) tea.Model {m := taskEditModel{config: config, create: task == nil}confirmDefault := truevar name stringvar desc stringvar status stringif task != nil {name = task.Namedesc = task.Description.Stringstatus = task.Status}m.form = huh.NewForm(huh.NewGroup(huh.NewInput().Key("name").Title("Name").Prompt("> ").Validate(huh.ValidateNotEmpty()).Value(&name),huh.NewText().Key("description").Title("Description").Value(&desc),huh.NewSelect[string]().Key("status").Options(huh.NewOptions("N", "P", "C", "H", "D")...).Title("Status ").Inline(true).Value(&status),// TODO make a select between "create and open" and "only create"// TODO select projecthuh.NewConfirm().Validate(func(v bool) error { // Should SHOULD closeif !v {return fmt.Errorf("Welp, finish up then")}return nil}).Affirmative("done").Negative("cancel").Inline(true).Value(&confirmDefault),),).WithWidth(45).WithShowHelp(false).WithShowErrors(false)return m}func (m taskEditModel) Init() tea.Cmd {return m.form.Init()}func (m taskEditModel) createTaskCmd() func() tea.Msg {m.config.mu.Lock()defer m.config.mu.Unlock()desc := sql.NullString{String: m.form.GetString("description")}if desc.String != "" {desc.Valid = true}dataset := db.CreateTaskParams{Name: m.form.GetString("name"),Description: desc,Status: m.form.GetString("status"),}return func() tea.Msg {m.config.queries.CreateTask(context.Background(), dataset)return editingDone(true)}}func (m taskEditModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {switch msg := msg.(type) {case tea.WindowSizeMsg:m.width = min(msg.Width, maxWidth) - m.config.styles.Base.GetHorizontalFrameSize()case editingDone:return m, changeView(newTaskList(m.config))}var cmds []tea.Cmd// Process the formform, cmd := m.form.Update(msg)if f, ok := form.(*huh.Form); ok {m.form = fcmds = append(cmds, cmd)}if m.form.State == huh.StateCompleted {// TODO handle abort// TODO handle updating an existingcmds = append(cmds, m.createTaskCmd())}return m, tea.Batch(cmds...)}func (m taskEditModel) View() string {var status strings := m.config.stylesv := strings.TrimSuffix(m.form.View(), "\n\n")form := m.config.lg.NewStyle().Margin(1, 0).Render(v)errors := m.form.Errors()header := m.appBoundaryView("Charm Employment Application")if len(errors) > 0 {header = m.appErrorBoundaryView(m.errorView())}body := lipgloss.JoinHorizontal(lipgloss.Top, form, status)footer := m.appBoundaryView(m.form.Help().ShortHelpView(m.form.KeyBinds()))if len(errors) > 0 {footer = m.appErrorBoundaryView("")}return s.Base.Render(header + "\n" + body + "\n\n" + footer)}func (m taskEditModel) errorView() string {var s stringfor _, err := range m.form.Errors() {s += err.Error()}return s}func (m taskEditModel) appBoundaryView(text string) string {return lipgloss.PlaceHorizontal(m.width,lipgloss.Left,m.config.styles.HeaderText.Render(text),lipgloss.WithWhitespaceChars("/"),lipgloss.WithWhitespaceForeground(indigo),)}func (m taskEditModel) appErrorBoundaryView(text string) string {return lipgloss.PlaceHorizontal(m.width,lipgloss.Left,m.config.styles.ErrorHeaderText.Render(text),lipgloss.WithWhitespaceChars("/"),lipgloss.WithWhitespaceForeground(red),)}