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
7 changes: 7 additions & 0 deletions cmd/entries/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ func EditCmd() *cobra.Command {
os.Exit(1)
}

db.SaveLastAction(storage.UndoAction{
Type: storage.ActionEdit,
EntryID: selectedEntry.ID,
ProjectName: selectedEntry.ProjectName,
Entry: selectedEntry,
})

fmt.Println()
ui.PrintSuccess(ui.EmojiSuccess, "Entry updated successfully")
ui.NewlineBelow()
Expand Down
7 changes: 7 additions & 0 deletions cmd/utilities/undo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var actionDescriptions = map[storage.ActionType]string{
storage.ActionResume: "Resumed tracking",
storage.ActionManual: "Created manual entry for",
storage.ActionDelete: "Deleted entry for",
storage.ActionEdit: "Edited entry for",
}

func UndoCmd() *cobra.Command {
Expand Down Expand Up @@ -104,6 +105,12 @@ func applyUndo(db *storage.Database, action *storage.UndoAction) error {
}
return db.RestoreDeletedEntry(action.Entry)

case storage.ActionEdit:
if action.Entry == nil {
return fmt.Errorf("no entry snapshot available to restore")
}
return db.UpdateTimeEntry(action.EntryID, action.Entry)

default:
return fmt.Errorf("unknown action type: %s", action.Type)
}
Expand Down
43 changes: 43 additions & 0 deletions cmd/utilities/undo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utilities

import (
"testing"
"time"

"github.com/DylanDevelops/tmpo/internal/storage"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -31,6 +32,7 @@ func TestUndoActionDescription(t *testing.T) {
{storage.ActionResume, "Resumed tracking"},
{storage.ActionManual, "Created manual entry for"},
{storage.ActionDelete, "Deleted entry for"},
{storage.ActionEdit, "Edited entry for"},
}

for _, tt := range tests {
Expand Down Expand Up @@ -91,6 +93,47 @@ func TestApplyUndo_Delete_ErrorWhenNoSnapshot(t *testing.T) {
assert.Contains(t, err.Error(), "no entry snapshot")
}

func TestApplyUndo_Edit_ErrorWhenNoSnapshot(t *testing.T) {
db := setupUndoTestDB(t)

action := &storage.UndoAction{Type: storage.ActionEdit, ProjectName: "proj", Entry: nil}
err := applyUndo(db, action)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no entry snapshot")
}

func TestApplyUndo_Edit_RestoresOriginalEntry(t *testing.T) {
db := setupUndoTestDB(t)

// Create and stop an entry, then capture its pre-edit snapshot.
orig, err := db.CreateEntry("proj", "before", nil, nil)
require.NoError(t, err)
require.NoError(t, db.StopEntry(orig.ID))

before, err := db.GetEntry(orig.ID)
require.NoError(t, err)

// Apply an edit that changes several fields.
edited := *before
newStart := before.StartTime.Add(-time.Hour)
newEnd := before.EndTime.Add(time.Hour)
edited.StartTime = newStart
edited.EndTime = &newEnd
edited.Description = "after"
require.NoError(t, db.UpdateTimeEntry(edited.ID, &edited))

// Undo should restore the original values.
action := &storage.UndoAction{Type: storage.ActionEdit, EntryID: before.ID, ProjectName: "proj", Entry: before}
require.NoError(t, applyUndo(db, action))

restored, err := db.GetEntry(orig.ID)
require.NoError(t, err)
assert.Equal(t, "before", restored.Description)
assert.True(t, restored.StartTime.Equal(before.StartTime), "start time should be restored")
require.NotNil(t, restored.EndTime)
assert.True(t, restored.EndTime.Equal(*before.EndTime), "end time should be restored")
}

func TestApplyUndo_UnknownType_ReturnsError(t *testing.T) {
db := setupUndoTestDB(t)

Expand Down
17 changes: 9 additions & 8 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,14 +433,15 @@ Revert the most recent time-tracking action in case of a mistake. A single confi

**Supported actions:**

| Action | What undo does |
|---------------|---------------------------------------------------|
| `tmpo start` | Deletes the entry that was just created |
| `tmpo stop` | Re-opens the stopped entry as a running timer |
| `tmpo pause` | Re-opens the paused entry as a running timer |
| `tmpo resume` | Deletes the new entry that was created to resume |
| `tmpo manual` | Deletes the manually created entry |
| `tmpo delete` | Restores the deleted entry with its original data |
| Action | What undo does |
|---------------|-------------------------------------------------------|
| `tmpo start` | Deletes the entry that was just created |
| `tmpo stop` | Re-opens the stopped entry as a running timer |
| `tmpo pause` | Re-opens the paused entry as a running timer |
| `tmpo resume` | Deletes the new entry that was created to resume |
| `tmpo manual` | Deletes the manually created entry |
| `tmpo delete` | Restores the deleted entry with its original data |
| `tmpo edit` | Restores the previous version of an edited time entry |

**Examples:**

Expand Down
1 change: 1 addition & 0 deletions internal/storage/undo.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
ActionResume ActionType = "resume"
ActionDelete ActionType = "delete"
ActionManual ActionType = "manual"
ActionEdit ActionType = "edit"
)

const lastActionKey = "last_action"
Expand Down
Loading