diff options
Diffstat (limited to 'internal/tui/clients_projects_box.go')
-rw-r--r-- | internal/tui/clients_projects_box.go | 247 |
1 files changed, 0 insertions, 247 deletions
diff --git a/internal/tui/clients_projects_box.go b/internal/tui/clients_projects_box.go deleted file mode 100644 index c52e964..0000000 --- a/internal/tui/clients_projects_box.go +++ /dev/null @@ -1,247 +0,0 @@ -package tui - -import ( - "fmt" - - "punchcard/internal/queries" - - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss/v2" -) - -// NewClientsProjectsModel creates a new clients/projects model -func NewClientsProjectsModel() ClientsProjectsModel { - return ClientsProjectsModel{ - selectedIndex: 0, - selectedIsClient: true, - } -} - -// Update handles messages for the clients/projects box -func (m ClientsProjectsModel) Update(msg tea.Msg) (ClientsProjectsModel, tea.Cmd) { - return m, nil -} - -// View renders the clients/projects box -func (m ClientsProjectsModel) View(width, height int, isSelected bool) string { - var content string - - if len(m.clients) == 0 { - content = "No clients found\n\nUse 'punch add client' to\nadd your first client." - } else { - content = m.renderClientsAndProjects() - } - - // Apply box styling - style := unselectedBoxStyle - if isSelected { - style = selectedBoxStyle - } - - title := "👥 Clients & Projects" - - return style.Width(width).Height(height).Render( - fmt.Sprintf("%s\n\n%s", title, content), - ) -} - -// renderClientsAndProjects renders the clients and their projects -func (m ClientsProjectsModel) renderClientsAndProjects() string { - var content string - - // Group projects by client - projectsByClient := make(map[int64][]queries.ListAllProjectsRow) - for _, project := range m.projects { - projectsByClient[project.ClientID] = append(projectsByClient[project.ClientID], project) - } - - // Track the absolute row index for selection highlighting - absoluteRowIndex := 0 - - for i, client := range m.clients { - if i > 0 { - content += "\n" - } - - // Client name with rate if available - clientLine := fmt.Sprintf("• %s", client.Name) - if client.BillableRate.Valid { - rateInDollars := float64(client.BillableRate.Int64) / 100.0 - clientLine += fmt.Sprintf(" ($%.2f/hr)", rateInDollars) - } - - // Highlight if this client is selected - clientStyle := lipgloss.NewStyle().Bold(true) - if m.selectedIsClient && m.selectedIndex == i { - clientStyle = clientStyle.Background(lipgloss.Color("62")).Foreground(lipgloss.Color("230")) - } - content += clientStyle.Render(clientLine) + "\n" - absoluteRowIndex++ - - // Projects for this client - clientProjects := projectsByClient[client.ID] - if len(clientProjects) == 0 { - content += " └── (no projects)\n" - } else { - for j, project := range clientProjects { - prefix := "├──" - if j == len(clientProjects)-1 { - prefix = "└──" - } - - projectLine := fmt.Sprintf(" %s %s", prefix, project.Name) - if project.BillableRate.Valid { - rateInDollars := float64(project.BillableRate.Int64) / 100.0 - projectLine += fmt.Sprintf(" ($%.2f/hr)", rateInDollars) - } - - // Highlight if this project is selected - // We need to check against the absolute project index in m.projects - projectStyle := lipgloss.NewStyle() - if !m.selectedIsClient { - // Find this project's index in the m.projects slice - for k, p := range m.projects { - if p.ID == project.ID && m.selectedIndex == k { - projectStyle = projectStyle.Background(lipgloss.Color("62")).Foreground(lipgloss.Color("230")) - break - } - } - } - content += projectStyle.Render(projectLine) + "\n" - } - } - } - - return content -} - -// UpdateData updates the clients and projects data -func (m ClientsProjectsModel) UpdateData(clients []queries.Client, projects []queries.ListAllProjectsRow) ClientsProjectsModel { - m.clients = clients - m.projects = projects - // Reset selection if we have new data - if len(clients) > 0 { - m.selectedIndex = 0 - m.selectedIsClient = true - } - return m -} - -// NextSelection moves to the next selectable row -func (m ClientsProjectsModel) NextSelection() ClientsProjectsModel { - totalRows := m.getTotalSelectableRows() - if totalRows == 0 { - return m - } - - currentIndex := m.getCurrentRowIndex() - if currentIndex < totalRows-1 { - m.setRowIndex(currentIndex + 1) - } - return m -} - -// PrevSelection moves to the previous selectable row -func (m ClientsProjectsModel) PrevSelection() ClientsProjectsModel { - totalRows := m.getTotalSelectableRows() - if totalRows == 0 { - return m - } - - currentIndex := m.getCurrentRowIndex() - if currentIndex > 0 { - m.setRowIndex(currentIndex - 1) - } - return m -} - -// getDisplayOrder returns items in the order they are displayed (tree structure) -func (m ClientsProjectsModel) getDisplayOrder() []ProjectsDisplayItem { - var items []ProjectsDisplayItem - - // Group projects by client - projectsByClient := make(map[int64][]queries.ListAllProjectsRow) - projectIndexByID := make(map[int64]int) - for i, project := range m.projects { - projectsByClient[project.ClientID] = append(projectsByClient[project.ClientID], project) - projectIndexByID[project.ID] = i - } - - // Build display order: client followed by its projects - for i, client := range m.clients { - // Add client - items = append(items, ProjectsDisplayItem{ - IsClient: true, - ClientIndex: i, - Client: &client, - }) - - // Add projects for this client - clientProjects := projectsByClient[client.ID] - for _, project := range clientProjects { - projectCopy := project // Copy to avoid reference issues - items = append(items, ProjectsDisplayItem{ - IsClient: false, - ClientIndex: i, - ProjectIndex: projectIndexByID[project.ID], - Project: &projectCopy, - }) - } - } - - return items -} - -// getTotalSelectableRows counts total items in display order -func (m ClientsProjectsModel) getTotalSelectableRows() int { - return len(m.getDisplayOrder()) -} - -// getCurrentRowIndex gets the current absolute row index in display order -func (m ClientsProjectsModel) getCurrentRowIndex() int { - displayOrder := m.getDisplayOrder() - - for i, item := range displayOrder { - if item.IsClient && m.selectedIsClient && item.ClientIndex == m.selectedIndex { - return i - } - if !item.IsClient && !m.selectedIsClient && item.ProjectIndex == m.selectedIndex { - return i - } - } - - return 0 // Default to first item if not found -} - -// setRowIndex sets the selection to the given absolute row index in display order -func (m *ClientsProjectsModel) setRowIndex(index int) { - displayOrder := m.getDisplayOrder() - if index < 0 || index >= len(displayOrder) { - return - } - - item := displayOrder[index] - if item.IsClient { - m.selectedIndex = item.ClientIndex - m.selectedIsClient = true - } else { - m.selectedIndex = item.ProjectIndex - m.selectedIsClient = false - } -} - -// GetSelectedClient returns the selected client if one is selected -func (m ClientsProjectsModel) GetSelectedClient() *queries.Client { - if m.selectedIsClient && m.selectedIndex < len(m.clients) { - return &m.clients[m.selectedIndex] - } - return nil -} - -// GetSelectedProject returns the selected project if one is selected -func (m ClientsProjectsModel) GetSelectedProject() *queries.ListAllProjectsRow { - if !m.selectedIsClient && m.selectedIndex < len(m.projects) { - return &m.projects[m.selectedIndex] - } - return nil -}
\ No newline at end of file |