summaryrefslogtreecommitdiff
path: root/internal/commands
diff options
context:
space:
mode:
Diffstat (limited to 'internal/commands')
-rw-r--r--internal/commands/archive.go141
-rw-r--r--internal/commands/archive_test.go517
-rw-r--r--internal/commands/in.go38
-rw-r--r--internal/commands/root.go2
4 files changed, 693 insertions, 5 deletions
diff --git a/internal/commands/archive.go b/internal/commands/archive.go
new file mode 100644
index 0000000..d6c9977
--- /dev/null
+++ b/internal/commands/archive.go
@@ -0,0 +1,141 @@
+package commands
+
+import (
+ "errors"
+ "fmt"
+
+ "git.tjp.lol/punchcard/internal/actions"
+ punchctx "git.tjp.lol/punchcard/internal/context"
+
+ "github.com/spf13/cobra"
+)
+
+func NewArchiveCmd() *cobra.Command {
+ var clientFlag, projectFlag string
+
+ cmd := &cobra.Command{
+ Use: "archive",
+ Short: "Archive a client or project",
+ Long: `Archive a client or project to hide it from the default view.
+
+Archived clients and projects will not be shown in the "Clients & Projects" pane
+by default, but can be toggled visible. All historical time entries remain accessible.
+
+Examples:
+ punch archive --client "Acme Corp"
+ punch archive --client 1
+ punch archive --project "Website Redesign"
+ punch archive --project 5`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ if clientFlag == "" && projectFlag == "" {
+ return errors.New("either --client or --project must be specified")
+ }
+
+ if clientFlag != "" && projectFlag != "" {
+ return errors.New("cannot specify both --client and --project")
+ }
+
+ q := punchctx.GetDB(cmd.Context())
+ if q == nil {
+ return fmt.Errorf("database not available in context")
+ }
+
+ a := actions.New(q)
+
+ if clientFlag != "" {
+ client, err := a.FindClient(cmd.Context(), clientFlag)
+ if err != nil {
+ return fmt.Errorf("failed to find client: %w", err)
+ }
+
+ if err := a.ArchiveClient(cmd.Context(), client.ID); err != nil {
+ return fmt.Errorf("failed to archive client: %w", err)
+ }
+
+ cmd.Printf("Archived client: %s\n", client.Name)
+ } else {
+ project, err := a.FindProject(cmd.Context(), projectFlag)
+ if err != nil {
+ return fmt.Errorf("failed to find project: %w", err)
+ }
+
+ if err := a.ArchiveProject(cmd.Context(), project.ID); err != nil {
+ return fmt.Errorf("failed to archive project: %w", err)
+ }
+
+ cmd.Printf("Archived project: %s\n", project.Name)
+ }
+
+ return nil
+ },
+ }
+
+ cmd.Flags().StringVarP(&clientFlag, "client", "c", "", "Client name or ID")
+ cmd.Flags().StringVarP(&projectFlag, "project", "p", "", "Project name or ID")
+
+ return cmd
+}
+
+func NewUnarchiveCmd() *cobra.Command {
+ var clientFlag, projectFlag string
+
+ cmd := &cobra.Command{
+ Use: "unarchive",
+ Short: "Unarchive a client or project",
+ Long: `Unarchive a client or project to show it in the default view again.
+
+Examples:
+ punch unarchive --client "Acme Corp"
+ punch unarchive --client 1
+ punch unarchive --project "Website Redesign"
+ punch unarchive --project 5`,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ if clientFlag == "" && projectFlag == "" {
+ return errors.New("either --client or --project must be specified")
+ }
+
+ if clientFlag != "" && projectFlag != "" {
+ return errors.New("cannot specify both --client and --project")
+ }
+
+ q := punchctx.GetDB(cmd.Context())
+ if q == nil {
+ return fmt.Errorf("database not available in context")
+ }
+
+ a := actions.New(q)
+
+ if clientFlag != "" {
+ client, err := a.FindClient(cmd.Context(), clientFlag)
+ if err != nil {
+ return fmt.Errorf("failed to find client: %w", err)
+ }
+
+ if err := a.UnarchiveClient(cmd.Context(), client.ID); err != nil {
+ return fmt.Errorf("failed to unarchive client: %w", err)
+ }
+
+ cmd.Printf("Unarchived client: %s\n", client.Name)
+ } else {
+ project, err := a.FindProject(cmd.Context(), projectFlag)
+ if err != nil {
+ return fmt.Errorf("failed to find project: %w", err)
+ }
+
+ if err := a.UnarchiveProject(cmd.Context(), project.ID); err != nil {
+ return fmt.Errorf("failed to unarchive project: %w", err)
+ }
+
+ cmd.Printf("Unarchived project: %s\n", project.Name)
+ }
+
+ return nil
+ },
+ }
+
+ cmd.Flags().StringVarP(&clientFlag, "client", "c", "", "Client name or ID")
+ cmd.Flags().StringVarP(&projectFlag, "project", "p", "", "Project name or ID")
+
+ return cmd
+}
+
diff --git a/internal/commands/archive_test.go b/internal/commands/archive_test.go
new file mode 100644
index 0000000..ae9ce1f
--- /dev/null
+++ b/internal/commands/archive_test.go
@@ -0,0 +1,517 @@
+package commands
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "git.tjp.lol/punchcard/internal/queries"
+)
+
+func TestArchiveCommand(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) (clientID, projectID int64)
+ args []string
+ expectedOutputs []string
+ expectError bool
+ errorContains string
+ }{
+ {
+ name: "archive client by name",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ return client.ID, 0
+ },
+ args: []string{"archive", "--client", "TestClient"},
+ expectedOutputs: []string{"Archived client: TestClient"},
+ expectError: false,
+ },
+ {
+ name: "archive client by ID",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ClientByID",
+ })
+ return client.ID, 0
+ },
+ args: []string{"archive", "--client", "1"},
+ expectedOutputs: []string{"Archived client: ClientByID"},
+ expectError: false,
+ },
+ {
+ name: "archive project by name",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "TestProject",
+ ClientID: client.ID,
+ })
+ return client.ID, project.ID
+ },
+ args: []string{"archive", "--project", "TestProject"},
+ expectedOutputs: []string{"Archived project: TestProject"},
+ expectError: false,
+ },
+ {
+ name: "archive project by ID",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ProjectByID",
+ ClientID: client.ID,
+ })
+ return client.ID, project.ID
+ },
+ args: []string{"archive", "--project", "1"},
+ expectedOutputs: []string{"Archived project: ProjectByID"},
+ expectError: false,
+ },
+ {
+ name: "archive without flags returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"archive"},
+ expectError: true,
+ errorContains: "either --client or --project must be specified",
+ },
+ {
+ name: "archive with both client and project flags returns error",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "TestProject",
+ ClientID: client.ID,
+ })
+ return client.ID, project.ID
+ },
+ args: []string{"archive", "--client", "TestClient", "--project", "TestProject"},
+ expectError: true,
+ errorContains: "cannot specify both --client and --project",
+ },
+ {
+ name: "archive nonexistent client returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"archive", "--client", "NonexistentClient"},
+ expectError: true,
+ errorContains: "failed to find client",
+ },
+ {
+ name: "archive nonexistent project returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"archive", "--project", "NonexistentProject"},
+ expectError: true,
+ errorContains: "failed to find project",
+ },
+ {
+ name: "archive already archived client succeeds",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "AlreadyArchived",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID, 0
+ },
+ args: []string{"archive", "--client", "AlreadyArchived"},
+ expectedOutputs: []string{"Archived client: AlreadyArchived"},
+ expectError: false,
+ },
+ {
+ name: "archive client using short flag",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ShortFlagClient",
+ })
+ return client.ID, 0
+ },
+ args: []string{"archive", "-c", "ShortFlagClient"},
+ expectedOutputs: []string{"Archived client: ShortFlagClient"},
+ expectError: false,
+ },
+ {
+ name: "archive project using short flag",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ShortFlagProject",
+ ClientID: client.ID,
+ })
+ return client.ID, project.ID
+ },
+ args: []string{"archive", "-p", "ShortFlagProject"},
+ expectedOutputs: []string{"Archived project: ShortFlagProject"},
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ tt.setupData(q)
+
+ output, err := executeCommandWithDB(t, q, tt.args...)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) {
+ t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error())
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ return
+ }
+
+ found := false
+ for _, expectedOutput := range tt.expectedOutputs {
+ if strings.Contains(output, expectedOutput) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("Expected output to contain one of %v, got %q", tt.expectedOutputs, output)
+ }
+ })
+ }
+}
+
+func TestUnarchiveCommand(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) (clientID, projectID int64)
+ args []string
+ expectedOutputs []string
+ expectError bool
+ errorContains string
+ }{
+ {
+ name: "unarchive client by name",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID, 0
+ },
+ args: []string{"unarchive", "--client", "ArchivedClient"},
+ expectedOutputs: []string{"Unarchived client: ArchivedClient"},
+ expectError: false,
+ },
+ {
+ name: "unarchive client by ID",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ClientByID",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID, 0
+ },
+ args: []string{"unarchive", "--client", "1"},
+ expectedOutputs: []string{"Unarchived client: ClientByID"},
+ expectError: false,
+ },
+ {
+ name: "unarchive project by name",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ArchivedProject",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return client.ID, project.ID
+ },
+ args: []string{"unarchive", "--project", "ArchivedProject"},
+ expectedOutputs: []string{"Unarchived project: ArchivedProject"},
+ expectError: false,
+ },
+ {
+ name: "unarchive project by ID",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ProjectByID",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return client.ID, project.ID
+ },
+ args: []string{"unarchive", "--project", "1"},
+ expectedOutputs: []string{"Unarchived project: ProjectByID"},
+ expectError: false,
+ },
+ {
+ name: "unarchive without flags returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"unarchive"},
+ expectError: true,
+ errorContains: "either --client or --project must be specified",
+ },
+ {
+ name: "unarchive with both client and project flags returns error",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "TestProject",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return client.ID, project.ID
+ },
+ args: []string{"unarchive", "--client", "TestClient", "--project", "TestProject"},
+ expectError: true,
+ errorContains: "cannot specify both --client and --project",
+ },
+ {
+ name: "unarchive nonexistent client returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"unarchive", "--client", "NonexistentClient"},
+ expectError: true,
+ errorContains: "failed to find client",
+ },
+ {
+ name: "unarchive nonexistent project returns error",
+ setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 },
+ args: []string{"unarchive", "--project", "NonexistentProject"},
+ expectError: true,
+ errorContains: "failed to find project",
+ },
+ {
+ name: "unarchive already active client succeeds",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ActiveClient",
+ })
+ return client.ID, 0
+ },
+ args: []string{"unarchive", "--client", "ActiveClient"},
+ expectedOutputs: []string{"Unarchived client: ActiveClient"},
+ expectError: false,
+ },
+ {
+ name: "unarchive client using short flag",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ShortFlagClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID, 0
+ },
+ args: []string{"unarchive", "-c", "ShortFlagClient"},
+ expectedOutputs: []string{"Unarchived client: ShortFlagClient"},
+ expectError: false,
+ },
+ {
+ name: "unarchive project using short flag",
+ setupData: func(q *queries.Queries) (int64, int64) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ShortFlagProject",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return client.ID, project.ID
+ },
+ args: []string{"unarchive", "-p", "ShortFlagProject"},
+ expectedOutputs: []string{"Unarchived project: ShortFlagProject"},
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ tt.setupData(q)
+
+ output, err := executeCommandWithDB(t, q, tt.args...)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) {
+ t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error())
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ return
+ }
+
+ found := false
+ for _, expectedOutput := range tt.expectedOutputs {
+ if strings.Contains(output, expectedOutput) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("Expected output to contain one of %v, got %q", tt.expectedOutputs, output)
+ }
+ })
+ }
+}
+
+func TestArchiveStateVerification(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) (entityID int64, isClient bool)
+ archiveAction func(*queries.Queries, int64)
+ verifyFunc func(*testing.T, *queries.Queries, int64, bool)
+ }{
+ {
+ name: "client is archived after archive command",
+ setupData: func(q *queries.Queries) (int64, bool) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ return client.ID, true
+ },
+ archiveAction: func(q *queries.Queries, id int64) {
+ _ = q.ArchiveClient(context.Background(), id)
+ },
+ verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) {
+ clients, err := q.FindClient(context.Background(), queries.FindClientParams{
+ ID: id,
+ })
+ if err != nil {
+ t.Fatalf("Failed to find client: %v", err)
+ }
+ if len(clients) != 1 {
+ t.Fatalf("Expected 1 client, got %d", len(clients))
+ }
+ if clients[0].Archived == 0 {
+ t.Errorf("Expected client to be archived")
+ }
+ },
+ },
+ {
+ name: "project is archived after archive command",
+ setupData: func(q *queries.Queries) (int64, bool) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "TestProject",
+ ClientID: client.ID,
+ })
+ return project.ID, false
+ },
+ archiveAction: func(q *queries.Queries, id int64) {
+ _ = q.ArchiveProject(context.Background(), id)
+ },
+ verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) {
+ projects, err := q.FindProject(context.Background(), queries.FindProjectParams{
+ ID: id,
+ })
+ if err != nil {
+ t.Fatalf("Failed to find project: %v", err)
+ }
+ if len(projects) != 1 {
+ t.Fatalf("Expected 1 project, got %d", len(projects))
+ }
+ if projects[0].Archived == 0 {
+ t.Errorf("Expected project to be archived")
+ }
+ },
+ },
+ {
+ name: "client is unarchived after unarchive command",
+ setupData: func(q *queries.Queries) (int64, bool) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID, true
+ },
+ archiveAction: func(q *queries.Queries, id int64) {
+ _ = q.UnarchiveClient(context.Background(), id)
+ },
+ verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) {
+ clients, err := q.FindClient(context.Background(), queries.FindClientParams{
+ ID: id,
+ })
+ if err != nil {
+ t.Fatalf("Failed to find client: %v", err)
+ }
+ if len(clients) != 1 {
+ t.Fatalf("Expected 1 client, got %d", len(clients))
+ }
+ if clients[0].Archived != 0 {
+ t.Errorf("Expected client to not be archived")
+ }
+ },
+ },
+ {
+ name: "project is unarchived after unarchive command",
+ setupData: func(q *queries.Queries) (int64, bool) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ArchivedProject",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return project.ID, false
+ },
+ archiveAction: func(q *queries.Queries, id int64) {
+ _ = q.UnarchiveProject(context.Background(), id)
+ },
+ verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) {
+ projects, err := q.FindProject(context.Background(), queries.FindProjectParams{
+ ID: id,
+ })
+ if err != nil {
+ t.Fatalf("Failed to find project: %v", err)
+ }
+ if len(projects) != 1 {
+ t.Fatalf("Expected 1 project, got %d", len(projects))
+ }
+ if projects[0].Archived != 0 {
+ t.Errorf("Expected project to not be archived")
+ }
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ entityID, isClient := tt.setupData(q)
+ tt.archiveAction(q, entityID)
+ tt.verifyFunc(t, q, entityID, isClient)
+ })
+ }
+}
diff --git a/internal/commands/in.go b/internal/commands/in.go
index 8c5025a..8a08edf 100644
--- a/internal/commands/in.go
+++ b/internal/commands/in.go
@@ -1,6 +1,7 @@
package commands
import (
+ "errors"
"fmt"
"git.tjp.lol/punchcard/internal/actions"
@@ -54,16 +55,43 @@ Examples:
var session *actions.TimerSession
var err error
+ // Try punching in without auto-unarchive first
if clientFlag == "" && projectFlag == "" {
- // Use most recent entry
- session, err = a.PunchInMostRecent(cmd.Context(), description, billableRate)
+ session, err = a.PunchInMostRecent(cmd.Context(), description, billableRate, false)
} else {
- // Use specified client/project
- session, err = a.PunchIn(cmd.Context(), clientFlag, projectFlag, description, billableRate)
+ session, err = a.PunchIn(cmd.Context(), clientFlag, projectFlag, description, billableRate, false)
}
+ // Handle archived errors by prompting user
if err != nil {
- return err
+ if errors.Is(err, actions.ErrArchivedClient) || errors.Is(err, actions.ErrArchivedProject) {
+ entityType := "client"
+ if errors.Is(err, actions.ErrArchivedProject) {
+ entityType = "project"
+ }
+
+ cmd.Printf("Warning: This %s is archived.\n", entityType)
+ cmd.Print("Continue and unarchive? (y/N): ")
+
+ var response string
+ _, err := fmt.Scanln(&response)
+ if err != nil || (response != "y" && response != "Y") {
+ return fmt.Errorf("operation cancelled")
+ }
+
+ // Retry with auto-unarchive enabled
+ if clientFlag == "" && projectFlag == "" {
+ session, err = a.PunchInMostRecent(cmd.Context(), description, billableRate, true)
+ } else {
+ session, err = a.PunchIn(cmd.Context(), clientFlag, projectFlag, description, billableRate, true)
+ }
+
+ if err != nil {
+ return err
+ }
+ } else {
+ return err
+ }
}
// Handle different response types
diff --git a/internal/commands/root.go b/internal/commands/root.go
index 9ac0790..6e61980 100644
--- a/internal/commands/root.go
+++ b/internal/commands/root.go
@@ -26,6 +26,8 @@ func NewRootCmd() *cobra.Command {
cmd.AddCommand(NewReportCmd())
cmd.AddCommand(NewSetCmd())
cmd.AddCommand(NewTUICmd())
+ cmd.AddCommand(NewArchiveCmd())
+ cmd.AddCommand(NewUnarchiveCmd())
return cmd
}