summaryrefslogtreecommitdiff
path: root/internal/tui/modal.go
diff options
context:
space:
mode:
authorT <t@tjp.lol>2025-08-08 10:11:07 -0600
committerT <t@tjp.lol>2025-08-08 10:11:19 -0600
commit54c791927b2851fb6739ed75897090c3c39ecca1 (patch)
tree8f97b3509ccffed1351fe3d101e70c1cf0d861a9 /internal/tui/modal.go
parenta7ee7f7280d593481501446008acc05e32abcd22 (diff)
create forms for clients and projects
Diffstat (limited to 'internal/tui/modal.go')
-rw-r--r--internal/tui/modal.go128
1 files changed, 106 insertions, 22 deletions
diff --git a/internal/tui/modal.go b/internal/tui/modal.go
index 88b2861..167b659 100644
--- a/internal/tui/modal.go
+++ b/internal/tui/modal.go
@@ -17,15 +17,23 @@ import (
type ModalType int
const (
- ModalTypeSearch ModalType = iota
- ModalTypeClient
+ ModalTypeClient ModalType = iota
ModalTypeProject
ModalTypeDeleteConfirmation
ModalTypeEntry
)
func (mt ModalType) newForm() Form {
- return NewEntryEditorForm()
+ switch mt {
+ case ModalTypeEntry:
+ return NewEntryEditorForm()
+ case ModalTypeClient:
+ return NewClientForm()
+ case ModalTypeProject:
+ return NewProjectForm()
+ }
+
+ return Form{}
}
type ModalBoxModel struct {
@@ -69,41 +77,64 @@ func (m ModalBoxModel) RenderCenteredOver(mainContent string, app AppModel) stri
func (m ModalBoxModel) Render() string {
switch m.Type {
- case ModalTypeSearch:
- return modalTitleStyle.Render("SEARCH BOX")
case ModalTypeEntry:
- return m.RenderEntryEditor()
+ return m.RenderFormModal("⏰ Time Entry")
case ModalTypeDeleteConfirmation:
return m.RenderDeleteConfirmation()
+ case ModalTypeClient:
+ return m.RenderFormModal("👤 Client")
+ case ModalTypeProject:
+ return m.RenderFormModal("📂 Project")
default: // REMOVE ME
return "DEFAULT CONTENT"
}
}
-func (m ModalBoxModel) RenderEntryEditor() string {
- return fmt.Sprintf("%s\n\n%s", modalTitleStyle.Render("✏️ Edit Time Entry"), m.form.View())
+func (m ModalBoxModel) RenderFormModal(title string) string {
+ return fmt.Sprintf(
+ "%s\n\n%s\n\n%s Delete %s Cancel",
+ modalTitleStyle.Render(title),
+ m.form.View(),
+ boldStyle.Render("[Enter]"),
+ boldStyle.Render("[Esc]"),
+ )
}
func (m ModalBoxModel) RenderDeleteConfirmation() string {
- title := modalTitleStyle.Render("🗑️ Delete Time Entry")
- content := "Are you sure you want to delete this time entry?\nThis action cannot be undone.\n\n[Enter] Delete [Esc] Cancel"
- return fmt.Sprintf("%s\n\n%s", title, content)
+ return fmt.Sprintf(
+ "%s\n\nAre you sure you want to delete this time entry?\nThis action cannot be undone.\n\n%s Delete %s Cancel",
+ modalTitleStyle.Render("🗑️ Delete Time Entry"),
+ boldStyle.Render("[Enter]"),
+ boldStyle.Render("[Esc]"),
+ )
+}
+
+func (m *ModalBoxModel) activateCreateProjectModal(am AppModel) {
+ m.activate(ModalTypeProject, 0)
+ if am.selectedBox == ProjectsBox && len(am.projectsBox.clients) > 0 {
+ client := am.projectsBox.clients[am.projectsBox.selectedClient]
+ m.form.fields[1].SetValue(client.Name)
+ }
+ m.form.fields[0].Focus()
}
-func (m *ModalBoxModel) activate(t ModalType) {
+func (m *ModalBoxModel) activate(t ModalType, editedID int64) {
m.Active = true
m.Type = t
m.form = t.newForm()
+ m.editedID = editedID
}
func (m *ModalBoxModel) deactivate() {
m.Active = false
}
-var modalTitleStyle = lipgloss.NewStyle().
- Bold(true)
+var (
+ boldStyle = lipgloss.NewStyle().Bold(true)
+ modalTitleStyle = boldStyle
+)
-func (m ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
+func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
switch m.Type {
case ModalTypeDeleteConfirmation:
err := am.queries.RemoveTimeEntry(context.Background(), m.editedID)
@@ -117,27 +148,80 @@ func (m ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
return reOpenModal()
}
- // Extract and validate form data
- params, hasErrors := m.validateAndParseForm(am)
+ params, hasErrors := m.validateAndParseEntryForm(am)
if hasErrors {
return reOpenModal()
}
- // Perform the edit
err := am.queries.EditTimeEntry(context.Background(), params)
if err != nil {
- // TODO: Handle edit error (could set form error)
+ m.form.err = err
+ return reOpenModal()
+ }
+
+ msg := am.refreshCmd()
+ return func() tea.Msg { return msg }
+ case ModalTypeClient:
+ if err := m.form.Error(); err != nil {
+ return reOpenModal()
+ }
+
+ var rate *float64
+ if value := m.form.fields[2].Value(); value != "" {
+ r, _ := strconv.ParseFloat(value, 64)
+ rate = &r
+ }
+
+ if m.editedID != 0 {
+ panic("editing a client not yet implemented")
+ }
+
+ if _, err := actions.New(am.queries).CreateClient(
+ context.Background(),
+ m.form.fields[0].Value(),
+ m.form.fields[1].Value(),
+ rate,
+ ); err != nil {
+ m.form.err = err
+ return reOpenModal()
+ }
+
+ msg := am.refreshCmd()
+ return func() tea.Msg { return msg }
+
+ case ModalTypeProject:
+ if err := m.form.Error(); err != nil {
+ return reOpenModal()
+ }
+
+ var rate *float64
+ if value := m.form.fields[2].Value(); value != "" {
+ r, _ := strconv.ParseFloat(value, 64)
+ rate = &r
+ }
+
+ if m.editedID != 0 {
+ panic("editing a project not yet implemented")
+ }
+
+ if _, err := actions.New(am.queries).CreateProject(
+ context.Background(),
+ m.form.fields[0].Value(),
+ m.form.fields[1].Value(),
+ rate,
+ ); err != nil {
+ m.form.err = err
return reOpenModal()
}
- // Success - close modal and refresh data
- return func() tea.Msg { return am.refreshCmd() }
+ msg := am.refreshCmd()
+ return func() tea.Msg { return msg }
}
return nil
}
-func (m *ModalBoxModel) validateAndParseForm(am AppModel) (queries.EditTimeEntryParams, bool) {
+func (m *ModalBoxModel) validateAndParseEntryForm(am AppModel) (queries.EditTimeEntryParams, bool) {
var params queries.EditTimeEntryParams
var hasErrors bool