Add UI to delete tracked times (#14100)
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
		
							parent
							
								
									6a696b93b1
								
							
						
					
					
						commit
						d38ae597e1
					
				
					 13 changed files with 123 additions and 4 deletions
				
			
		|  | @ -136,6 +136,8 @@ type Comment struct { | |||
| 	MilestoneID      int64 | ||||
| 	OldMilestone     *Milestone `xorm:"-"` | ||||
| 	Milestone        *Milestone `xorm:"-"` | ||||
| 	TimeID           int64 | ||||
| 	Time             *TrackedTime `xorm:"-"` | ||||
| 	AssigneeID       int64 | ||||
| 	RemovedAssignee  bool | ||||
| 	Assignee         *User `xorm:"-"` | ||||
|  | @ -541,6 +543,16 @@ func (c *Comment) LoadDepIssueDetails() (err error) { | |||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // LoadTime loads the associated time for a CommentTypeAddTimeManual
 | ||||
| func (c *Comment) LoadTime() error { | ||||
| 	if c.Time != nil || c.TimeID == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	var err error | ||||
| 	c.Time, err = GetTrackedTimeByID(c.TimeID) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (c *Comment) loadReactions(e Engine, repo *Repository) (err error) { | ||||
| 	if c.Reactions != nil { | ||||
| 		return nil | ||||
|  | @ -692,6 +704,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err | |||
| 		MilestoneID:      opts.MilestoneID, | ||||
| 		OldProjectID:     opts.OldProjectID, | ||||
| 		ProjectID:        opts.ProjectID, | ||||
| 		TimeID:           opts.TimeID, | ||||
| 		RemovedAssignee:  opts.RemovedAssignee, | ||||
| 		AssigneeID:       opts.AssigneeID, | ||||
| 		AssigneeTeamID:   opts.AssigneeTeamID, | ||||
|  | @ -859,6 +872,7 @@ type CreateCommentOptions struct { | |||
| 	MilestoneID      int64 | ||||
| 	OldProjectID     int64 | ||||
| 	ProjectID        int64 | ||||
| 	TimeID           int64 | ||||
| 	AssigneeID       int64 | ||||
| 	AssigneeTeamID   int64 | ||||
| 	RemovedAssignee  bool | ||||
|  |  | |||
|  | @ -100,6 +100,7 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { | |||
| 			Repo:    issue.Repo, | ||||
| 			Content: SecToTime(timediff), | ||||
| 			Type:    CommentTypeStopTracking, | ||||
| 			TimeID:  tt.ID, | ||||
| 		}); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  |  | |||
|  | @ -162,6 +162,7 @@ func AddTime(user *User, issue *Issue, amount int64, created time.Time) (*Tracke | |||
| 		Doer:    user, | ||||
| 		Content: SecToTime(amount), | ||||
| 		Type:    CommentTypeAddTimeManual, | ||||
| 		TimeID:  t.ID, | ||||
| 	}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  |  | |||
|  | @ -292,6 +292,8 @@ var migrations = []Migration{ | |||
| 	NewMigration("Add Sorting to ProjectBoard table", addSortingColToProjectBoard), | ||||
| 	// v172 -> v173
 | ||||
| 	NewMigration("Add sessions table for go-chi/session", addSessionTable), | ||||
| 	// v173 -> v174
 | ||||
| 	NewMigration("Add time_id column to Comment", addTimeIDCommentColumn), | ||||
| } | ||||
| 
 | ||||
| // GetCurrentDBVersion returns the current db version
 | ||||
|  |  | |||
							
								
								
									
										22
									
								
								models/migrations/v173.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								models/migrations/v173.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| // Copyright 2021 The Gitea Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a MIT-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package migrations | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"xorm.io/xorm" | ||||
| ) | ||||
| 
 | ||||
| func addTimeIDCommentColumn(x *xorm.Engine) error { | ||||
| 	type Comment struct { | ||||
| 		TimeID int64 | ||||
| 	} | ||||
| 
 | ||||
| 	if err := x.Sync2(new(Comment)); err != nil { | ||||
| 		return fmt.Errorf("Sync2: %v", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | @ -1163,6 +1163,7 @@ issues.stop_tracking_history = `stopped working %s` | |||
| issues.cancel_tracking = Discard | ||||
| issues.cancel_tracking_history = `cancelled time tracking %s` | ||||
| issues.add_time = Manually Add Time | ||||
| issues.del_time = Delete this time log | ||||
| issues.add_time_short = Add Time | ||||
| issues.add_time_cancel = Cancel | ||||
| issues.add_time_history = `added spent time %s` | ||||
|  |  | |||
|  | @ -1416,6 +1416,10 @@ func ViewIssue(ctx *context.Context) { | |||
| 				ctx.ServerError("LoadPushCommits", err) | ||||
| 				return | ||||
| 			} | ||||
| 		} else if comment.Type == models.CommentTypeAddTimeManual || | ||||
| 			comment.Type == models.CommentTypeStopTracking { | ||||
| 			// drop error since times could be pruned from DB..
 | ||||
| 			_ = comment.LoadTime() | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,13 +10,13 @@ import ( | |||
| 
 | ||||
| 	"code.gitea.io/gitea/models" | ||||
| 	"code.gitea.io/gitea/modules/context" | ||||
| 	auth "code.gitea.io/gitea/modules/forms" | ||||
| 	"code.gitea.io/gitea/modules/forms" | ||||
| 	"code.gitea.io/gitea/modules/web" | ||||
| ) | ||||
| 
 | ||||
| // AddTimeManually tracks time manually
 | ||||
| func AddTimeManually(c *context.Context) { | ||||
| 	form := web.GetForm(c).(*auth.AddTimeManuallyForm) | ||||
| 	form := web.GetForm(c).(*forms.AddTimeManuallyForm) | ||||
| 	issue := GetActionIssue(c) | ||||
| 	if c.Written() { | ||||
| 		return | ||||
|  | @ -48,3 +48,39 @@ func AddTimeManually(c *context.Context) { | |||
| 
 | ||||
| 	c.Redirect(url, http.StatusSeeOther) | ||||
| } | ||||
| 
 | ||||
| // DeleteTime deletes tracked time
 | ||||
| func DeleteTime(c *context.Context) { | ||||
| 	issue := GetActionIssue(c) | ||||
| 	if c.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 	if !c.Repo.CanUseTimetracker(issue, c.User) { | ||||
| 		c.NotFound("CanUseTimetracker", nil) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	t, err := models.GetTrackedTimeByID(c.ParamsInt64(":timeid")) | ||||
| 	if err != nil { | ||||
| 		if models.IsErrNotExist(err) { | ||||
| 			c.NotFound("time not found", err) | ||||
| 			return | ||||
| 		} | ||||
| 		c.Error(http.StatusInternalServerError, "GetTrackedTimeByID", err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// only OP or admin may delete
 | ||||
| 	if !c.IsSigned || (!c.IsUserSiteAdmin() && c.User.ID != t.UserID) { | ||||
| 		c.Error(http.StatusForbidden, "not allowed") | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err = models.DeleteTime(t); err != nil { | ||||
| 		c.ServerError("DeleteTime", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.Flash.Success(c.Tr("repo.issues.del_time_history", models.SecToTime(t.Time))) | ||||
| 	c.Redirect(issue.HTMLURL()) | ||||
| } | ||||
|  |  | |||
|  | @ -723,6 +723,7 @@ func RegisterRoutes(m *web.Route) { | |||
| 				m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) | ||||
| 				m.Group("/times", func() { | ||||
| 					m.Post("/add", bindIgnErr(auth.AddTimeManuallyForm{}), repo.AddTimeManually) | ||||
| 					m.Post("/{timeid}/delete", repo.DeleteTime) | ||||
| 					m.Group("/stopwatch", func() { | ||||
| 						m.Post("/toggle", repo.IssueStopwatch) | ||||
| 						m.Post("/cancel", repo.CancelStopwatch) | ||||
|  |  | |||
|  | @ -276,6 +276,7 @@ | |||
| 				<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> | ||||
| 				{{$.i18n.Tr "repo.issues.stop_tracking_history"  $createdStr | Safe}} | ||||
| 			</span> | ||||
| 			{{ template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" . }} | ||||
| 			<div class="detail"> | ||||
| 				{{svg "octicon-clock"}} | ||||
| 				<span class="text grey">{{.Content}}</span> | ||||
|  | @ -291,6 +292,7 @@ | |||
| 				<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> | ||||
| 				{{$.i18n.Tr "repo.issues.add_time_history"  $createdStr | Safe}} | ||||
| 			</span> | ||||
| 			{{ template "repo/issue/view_content/comments_delete_time" Dict "ctx" $ "comment" . }} | ||||
| 			<div class="detail"> | ||||
| 				{{svg "octicon-clock"}} | ||||
| 				<span class="text grey">{{.Content}}</span> | ||||
|  |  | |||
							
								
								
									
										21
									
								
								templates/repo/issue/view_content/comments_delete_time.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								templates/repo/issue/view_content/comments_delete_time.tmpl
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| {{ if .comment.Time }} {{/* compatibility with time comments made before v1.14 */}} | ||||
| 	{{ if (not .comment.Time.Deleted) }} | ||||
| 		{{ if (or .ctx.IsAdmin (and .ctx.IsSigned (eq .ctx.SignedUserID .comment.PosterID))) }} | ||||
| 			<span class="ui float right"> | ||||
| 				<div class="ui mini modal issue-delete-time-modal" data-id="{{.comment.Time.ID}}"> | ||||
| 					<form method="POST" class="delete-time-form" action="{{.ctx.RepoLink}}/issues/{{.ctx.Issue.Index}}/times/{{.comment.TimeID}}/delete"> | ||||
| 						{{.ctx.CsrfTokenHtml}} | ||||
| 					</form> | ||||
| 					<div class="header">{{.ctx.i18n.Tr "repo.issues.del_time"}}</div> | ||||
| 					<div class="actions"> | ||||
| 						<div class="ui red approve button">{{.ctx.i18n.Tr "repo.issues.context.delete"}}</div> | ||||
| 						<div class="ui cancel button">{{.ctx.i18n.Tr "repo.issues.add_time_cancel"}}</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<button class="ui icon button compact mini issue-delete-time poping up" data-id="{{.comment.Time.ID}}" data-content="{{.ctx.i18n.Tr "repo.issues.del_time"}}" data-position="top right" data-variation="tiny inverted"> | ||||
| 					{{svg "octicon-trashcan"}} | ||||
| 				</button> | ||||
| 			</span> | ||||
| 		{{end}} | ||||
| 	{{end}} | ||||
| {{end}} | ||||
|  | @ -348,7 +348,7 @@ | |||
| 							{{end}} | ||||
| 							<div class="ui buttons two fluid"> | ||||
| 								<button class="ui button poping up issue-start-time" data-content='{{.i18n.Tr "repo.issues.start_tracking"}}' data-position="top center" data-variation="small inverted">{{.i18n.Tr "repo.issues.start_tracking_short"}}</button> | ||||
| 								<div class="ui mini modal"> | ||||
| 								<div class="ui mini modal issue-start-time-modal"> | ||||
| 									<div class="header">{{.i18n.Tr "repo.issues.add_time"}}</div> | ||||
| 									<div class="content"> | ||||
| 										<form method="POST" id="add_time_manual_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/times/add" class="ui action input fluid"> | ||||
|  |  | |||
|  | @ -3203,12 +3203,17 @@ function initVueApp() { | |||
| 
 | ||||
| function initIssueTimetracking() { | ||||
|   $(document).on('click', '.issue-add-time', () => { | ||||
|     $('.mini.modal').modal({ | ||||
|     $('.issue-start-time-modal').modal({ | ||||
|       duration: 200, | ||||
|       onApprove() { | ||||
|         $('#add_time_manual_form').trigger('submit'); | ||||
|       } | ||||
|     }).modal('show'); | ||||
|     $('.issue-start-time-modal input').on('keydown', (e) => { | ||||
|       if ((e.keyCode || e.key) === 13) { | ||||
|         $('#add_time_manual_form').trigger('submit'); | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|   $(document).on('click', '.issue-start-time, .issue-stop-time', () => { | ||||
|     $('#toggle_stopwatch_form').trigger('submit'); | ||||
|  | @ -3216,6 +3221,15 @@ function initIssueTimetracking() { | |||
|   $(document).on('click', '.issue-cancel-time', () => { | ||||
|     $('#cancel_stopwatch_form').trigger('submit'); | ||||
|   }); | ||||
|   $(document).on('click', 'button.issue-delete-time', function () { | ||||
|     const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`; | ||||
|     $(sel).modal({ | ||||
|       duration: 200, | ||||
|       onApprove() { | ||||
|         $(`${sel} form`).trigger('submit'); | ||||
|       } | ||||
|     }).modal('show'); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function initFilterBranchTagDropdown(selector) { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue