Skip to content
Open
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
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,19 +543,71 @@ EOF
The `worklog` command provides a list of sub-commands to manage issue worklog (timelog).

##### Add
The `add` command lets you add a worklog to an issue. The command supports markdown for worklog comments.
The `add` command lets you add a worklog to an issue. The command supports markdown for worklog comments and returns the created worklog ID.

```sh
# Add a worklog using an interactive prompt
$ jira issue worklog add

# Pass required parameters and use --no-input to skip prompt
$ jira issue worklog add ISSUE-1 "2d 3h 30m" --no-input
✓ Worklog 10001 added to issue "ISSUE-1"

# You can add a comment using --comment flag when adding a worklog
$ jira issue worklog add ISSUE-1 "10m" --comment "This is a comment" --no-input
```

##### List
The `list` command displays all worklogs for an issue. Supports both table and plain text output formats.

```sh
# List worklogs in table format (default)
$ jira issue worklog list ISSUE-1

# List worklogs with detailed information
$ jira issue worklog list ISSUE-1 --plain

# Using the alias
$ jira issue worklog ls ISSUE-1
```

##### Edit
The `edit` command allows you to update an existing worklog. You can modify the time spent, comment, and start date.

```sh
# Edit a worklog interactively (select from list)
$ jira issue worklog edit ISSUE-1

# Edit a specific worklog with new time
$ jira issue worklog edit ISSUE-1 10001 "3h 30m" --no-input

# Edit worklog with new comment and start date
$ jira issue worklog edit ISSUE-1 10001 "2h" \
--comment "Updated work description" \
--started "2024-11-05 09:30:00"

# Using the alias
$ jira issue worklog update ISSUE-1 10001 "4h"
```

##### Delete
The `delete` command removes a worklog from an issue. By default, it asks for confirmation before deleting.

```sh
# Delete a worklog interactively (select from list)
$ jira issue worklog delete ISSUE-1

# Delete a specific worklog with confirmation
$ jira issue worklog delete ISSUE-1 10001

# Delete without confirmation prompt (use with caution)
$ jira issue worklog delete ISSUE-1 10001 --force

# Using the aliases
$ jira issue worklog remove ISSUE-1 10001
$ jira issue worklog rm ISSUE-1 10001 -f
```

### Epic
Epics are displayed in an explorer view by default. You can output the results in a table view using the `--table` flag.
When viewing epic issues, you can use all filters available for the issue command.
Expand Down
4 changes: 2 additions & 2 deletions internal/cmd/issue/worklog/add/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func add(cmd *cobra.Command, args []string) {
}
}

err := func() error {
worklog, err := func() (*jira.Worklog, error) {
s := cmdutil.Info("Adding a worklog")
defer s.Stop()

Expand All @@ -107,7 +107,7 @@ func add(cmd *cobra.Command, args []string) {

server := viper.GetString("server")

cmdutil.Success("Worklog added to issue %q", ac.params.issueKey)
cmdutil.Success("Worklog %s added to issue %q", worklog.ID, ac.params.issueKey)
fmt.Printf("%s\n", cmdutil.GenerateServerBrowseURL(server, ac.params.issueKey))
}

Expand Down
185 changes: 185 additions & 0 deletions internal/cmd/issue/worklog/delete/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package delete

import (
"fmt"

"github.com/AlecAivazis/survey/v2"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/ankitpokhrel/jira-cli/api"
"github.com/ankitpokhrel/jira-cli/internal/cmdutil"
"github.com/ankitpokhrel/jira-cli/internal/query"
"github.com/ankitpokhrel/jira-cli/pkg/jira"
)

const (
helpText = `Delete removes a worklog from an issue.`
examples = `$ jira issue worklog delete

# Delete a specific worklog
$ jira issue worklog delete ISSUE-1 10001

# Delete worklog without confirmation prompt
$ jira issue worklog delete ISSUE-1 10001 --force

# Delete worklog interactively (select from list)
$ jira issue worklog delete ISSUE-1`
)

// NewCmdWorklogDelete is a worklog delete command.
func NewCmdWorklogDelete() *cobra.Command {
cmd := cobra.Command{
Use: "delete ISSUE-KEY [WORKLOG-ID]",
Short: "Delete a worklog from an issue",
Long: helpText,
Example: examples,
Aliases: []string{"remove", "rm"},
Annotations: map[string]string{
"help:args": "ISSUE-KEY\tIssue key of the source issue, eg: ISSUE-1\n" +
"WORKLOG-ID\tID of the worklog to delete (optional, will prompt to select if not provided)",
},
Run: deleteWorklog,
}

cmd.Flags().SortFlags = false

cmd.Flags().BoolP("force", "f", false, "Skip confirmation prompt")

return &cmd
}

func deleteWorklog(cmd *cobra.Command, args []string) {
params := parseArgsAndFlags(args, cmd.Flags())
client := api.DefaultClient(params.debug)
dc := deleteCmd{
client: client,
params: params,
}

cmdutil.ExitIfError(dc.setIssueKey())
cmdutil.ExitIfError(dc.setWorklogID())

if !params.force {
var confirm bool
prompt := &survey.Confirm{
Message: fmt.Sprintf("Are you sure you want to delete worklog %s from issue %s?",
dc.params.worklogID, dc.params.issueKey),
Default: false,
}
if err := survey.AskOne(prompt, &confirm); err != nil {
cmdutil.Failed("Confirmation failed: %s", err.Error())
}
if !confirm {
cmdutil.Failed("Action cancelled")
}
}

err := func() error {
s := cmdutil.Info("Deleting worklog")
defer s.Stop()

return client.DeleteIssueWorklog(dc.params.issueKey, dc.params.worklogID)
}()
cmdutil.ExitIfError(err)

server := viper.GetString("server")

cmdutil.Success("Worklog deleted from issue %q", dc.params.issueKey)
fmt.Printf("%s\n", cmdutil.GenerateServerBrowseURL(server, dc.params.issueKey))
}

type deleteParams struct {
issueKey string
worklogID string
force bool
debug bool
}

func parseArgsAndFlags(args []string, flags query.FlagParser) *deleteParams {
var issueKey, worklogID string

nargs := len(args)
if nargs >= 1 {
issueKey = cmdutil.GetJiraIssueKey(viper.GetString("project.key"), args[0])
}
if nargs >= 2 {
worklogID = args[1]
}

debug, err := flags.GetBool("debug")
cmdutil.ExitIfError(err)

force, err := flags.GetBool("force")
cmdutil.ExitIfError(err)

return &deleteParams{
issueKey: issueKey,
worklogID: worklogID,
force: force,
debug: debug,
}
}

type deleteCmd struct {
client *jira.Client
params *deleteParams
}

func (dc *deleteCmd) setIssueKey() error {
if dc.params.issueKey != "" {
return nil
}

var ans string

qs := &survey.Question{
Name: "issueKey",
Prompt: &survey.Input{Message: "Issue key"},
Validate: survey.Required,
}
if err := survey.Ask([]*survey.Question{qs}, &ans); err != nil {
return err
}
dc.params.issueKey = cmdutil.GetJiraIssueKey(viper.GetString("project.key"), ans)

return nil
}

func (dc *deleteCmd) setWorklogID() error {
if dc.params.worklogID != "" {
return nil
}

// Fetch worklogs for the issue
worklogs, err := dc.client.GetIssueWorklogs(dc.params.issueKey)
if err != nil {
return err
}

if worklogs.Total == 0 {
return fmt.Errorf("no worklogs found for issue %s", dc.params.issueKey)
}

// Create options for selection
options := make([]string, len(worklogs.Worklogs))
for i, wl := range worklogs.Worklogs {
options[i] = fmt.Sprintf("%s - %s by %s (%s)", wl.ID, wl.TimeSpent, wl.Author.Name, wl.Started)
}

var selected string
prompt := &survey.Select{
Message: "Select worklog to delete:",
Options: options,
}
if err := survey.AskOne(prompt, &selected); err != nil {
return err
}

// Extract worklog ID from selection (format: "ID - ...")
var id string
_, _ = fmt.Sscanf(selected, "%s -", &id)
dc.params.worklogID = id

return nil
}
Loading