Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions cmd/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package cmd

import (
"fmt"
"os"

"github.com/DylanDevelops/tmpo/internal/storage"
"github.com/DylanDevelops/tmpo/internal/ui"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)

var showAllProjectsDelete bool

var deleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete a time entry",
Long: `Delete a time entry using an interactive menu.`,
Run: func(cmd *cobra.Command, args []string) {
ui.NewlineAbove()
ui.PrintSuccess("🗑️", "Delete Time Entry")
fmt.Println()

db, err := storage.Initialize()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}
defer db.Close()

var entries []*storage.TimeEntry
var projectName string

if showAllProjectsDelete {
// Show project selection first
projects, err := db.GetAllProjects()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

if len(projects) == 0 {
ui.PrintError(ui.EmojiError, "No time entries found")
ui.NewlineBelow()
os.Exit(1)
}

projectPrompt := promptui.Select{
Label: "Select project",
Items: projects,
}

_, selectedProject, err := projectPrompt.Run()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

projectName = selectedProject
} else {
// Use current project
detectedProject, err := DetectProjectName()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("detecting project: %v", err))
os.Exit(1)
}
projectName = detectedProject
}

// Get all entries for the selected/detected project
entries, err = db.GetEntriesByProject(projectName)
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

if len(entries) == 0 {
ui.PrintError(ui.EmojiError, fmt.Sprintf("No time entries found for project '%s'", projectName))
if !showAllProjectsDelete {
ui.PrintMuted(0, "Use 'tmpo delete --show-all-projects' to see entries from all projects")
}
ui.NewlineBelow()
os.Exit(1)
}

// Format entries for selection
templates := &promptui.SelectTemplates{
Label: "{{ . }}",
Active: "▸ {{ .Label }}",
Inactive: " {{ .Label }}",
Selected: "{{ .Label }}",
}

type entryItem struct {
Label string
Entry *storage.TimeEntry
}

var items []entryItem
for _, entry := range entries {
label := formatEntryLabelForDelete(entry)
items = append(items, entryItem{Label: label, Entry: entry})
}

entryPrompt := promptui.Select{
Label: "Select entry to delete",
Items: items,
Templates: templates,
}

idx, _, err := entryPrompt.Run()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

selectedEntry := items[idx].Entry

// Show entry details and confirmation
fmt.Println()
ui.PrintWarning(ui.EmojiWarning, "You are about to delete this entry:")
fmt.Println()
ui.PrintInfo(4, "ID", fmt.Sprintf("%d", selectedEntry.ID))
ui.PrintInfo(4, "Project", selectedEntry.ProjectName)
ui.PrintInfo(4, "Start", selectedEntry.StartTime.Format("Jan 2, 2006 at 3:04 PM"))
if selectedEntry.EndTime != nil {
ui.PrintInfo(4, "End", selectedEntry.EndTime.Format("Jan 2, 2006 at 3:04 PM"))
ui.PrintInfo(4, "Duration", ui.FormatDuration(selectedEntry.Duration()))
} else {
ui.PrintInfo(4, "Status", ui.Warning("Running"))
}
if selectedEntry.Description != "" {
ui.PrintInfo(4, "Description", selectedEntry.Description)
}
fmt.Println()

// Confirm deletion
confirmPrompt := promptui.Select{
Label: "Are you sure you want to delete this entry?",
Items: []string{"No", "Yes"},
}

_, result, err := confirmPrompt.Run()
if err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

if result == "No" {
ui.PrintWarning(ui.EmojiWarning, "Deletion cancelled")
ui.NewlineBelow()
os.Exit(0)
}

// Delete from database
if err := db.DeleteTimeEntry(selectedEntry.ID); err != nil {
ui.PrintError(ui.EmojiError, fmt.Sprintf("%v", err))
os.Exit(1)
}

fmt.Println()
ui.PrintSuccess(ui.EmojiSuccess, "Entry deleted successfully")
ui.NewlineBelow()
},
}

// formatEntryLabelForDelete formats a time entry for display in the delete selection list
// Shows running entries differently than completed ones
func formatEntryLabelForDelete(entry *storage.TimeEntry) string {
startStr := entry.StartTime.Format("2006-01-02 3:04 PM")

if entry.EndTime == nil {
// Running entry
description := entry.Description
if description == "" {
description = "(no description)"
}
return fmt.Sprintf("%s → Running - %s", startStr, description)
}

// Completed entry
endStr := entry.EndTime.Format("3:04 PM")
duration := entry.Duration()
durationStr := ui.FormatDuration(duration)

description := entry.Description
if description == "" {
description = "(no description)"
}

return fmt.Sprintf("%s → %s (%s) - %s", startStr, endStr, durationStr, description)
}

func init() {
rootCmd.AddCommand(deleteCmd)

deleteCmd.Flags().BoolVar(&showAllProjectsDelete, "show-all-projects", false, "Show project selection before entry selection")
}
Loading
Loading