Worktime tracking for the organization level (#19808)

Dear Gitea team,

first of all, thanks for the great work you're doing with this project.

I'm planning to introduce Gitea at a client site, and noticed that while
there is time recording, there are no project-manager-friendly reports
to actually make use of that data, as were also mentioned by others in
#4870 #8684 and #13531.

Since I had a little time last weekend, I had put together something
that I hope to be a useful contribution to this great project (while of
course useful for me too).

This PR adds a new "Worktime" tab to the Organisation level. There is a
date range selector (by default set to the current month), and there are
three possible views:

- by repository,
- by milestone, and
- by team member.

Happy to receive any feedback!

There are several possible future improvements of course (predefined
date ranges, charts, a member time sheet, matrix of repos/members, etc)
but I hope that even in this relatively simple state this would be
useful to lots of people.

<img width="1161" alt="Screen Shot 2022-05-25 at 22 12 58"
src="https://user-images.githubusercontent.com/118010/170366976-af00c7af-c4f3-4117-86d7-00356d6797a5.png">

Keep up the good work!

Kristof

---------

Co-authored-by: user <user@kk-git1>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
K Kovacs 2025-02-02 18:51:12 +01:00 committed by GitHub
parent 869f8fdbe4
commit 34692a20b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 612 additions and 15 deletions

View file

@ -45,6 +45,11 @@
</a>
{{end}}
{{if .IsOrganizationOwner}}
<a class="{{if $.PageIsOrgTimes}}active{{end}} item" href="{{$.OrgLink}}/worktime">
{{svg "octicon-clock"}} {{ctx.Locale.Tr "org.worktime"}}
</a>
{{end}}
{{if .IsOrganizationOwner}}
<span class="item-flex-space"></span>
<a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings">
{{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}}

View file

@ -0,0 +1,40 @@
{{template "base/head" .}}
<div class="page-content organization times">
{{template "org/header" .}}
<div class="ui container">
<div class="ui grid">
<div class="three wide column">
<form class="ui form" method="get">
<input type="hidden" name="by" value="{{$.WorktimeBy}}">
<div class="field">
<label>{{ctx.Locale.Tr "org.worktime.date_range_start"}}</label>
<input type="date" name="from" value="{{.RangeFrom}}">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "org.worktime.date_range_end"}}</label>
<input type="date" name="to" value="{{.RangeTo}}">
</div>
<button class="ui primary button">{{ctx.Locale.Tr "org.worktime.query"}}</button>
</form>
</div>
<div class="thirteen wide column">
<div class="ui column">
<div class="ui compact small menu">
{{$queryParams := QueryBuild "from" .RangeFrom "to" .RangeTo}}
<a class="{{Iif .WorktimeByRepos "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=repos&{{$queryParams}}">{{svg "octicon-repo"}} {{ctx.Locale.Tr "org.worktime.by_repositories"}}</a>
<a class="{{Iif .WorktimeByMilestones "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=milestones&{{$queryParams}}">{{svg "octicon-milestone"}} {{ctx.Locale.Tr "org.worktime.by_milestones"}}</a>
<a class="{{Iif .WorktimeByMembers "active"}} item" href="{{$.Org.OrganisationLink}}/worktime?by=members&{{$queryParams}}">{{svg "octicon-people"}} {{ctx.Locale.Tr "org.worktime.by_members"}}</a>
</div>
</div>
{{if .WorktimeByRepos}}
{{template "org/worktime/table_repos" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
{{else if .WorktimeByMilestones}}
{{template "org/worktime/table_milestones" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
{{else if .WorktimeByMembers}}
{{template "org/worktime/table_members" dict "Org" .Org "WorktimeSumResult" .WorktimeSumResult}}
{{end}}
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View file

@ -0,0 +1,16 @@
<table class="ui table">
<thead>
<tr>
<th>{{ctx.Locale.Tr "org.members.member"}}</th>
<th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
</tr>
</thead>
<tbody>
{{range $.WorktimeSumResult}}
<tr>
<td>{{svg "octicon-person"}} <a href="{{AppSubUrl}}/{{PathEscape .UserName}}">{{.UserName}}</a></td>
<td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
</tr>
{{end}}
</tbody>
</table>

View file

@ -0,0 +1,28 @@
<table class="ui table">
<thead>
<tr>
<th>{{ctx.Locale.Tr "repository"}}</th>
<th>{{ctx.Locale.Tr "repo.milestone"}}</th>
<th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
</tr>
</thead>
<tbody>
{{range $.WorktimeSumResult}}
<tr>
<td>
{{if not .HideRepoName}}
{{svg "octicon-repo"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/issues">{{.RepoName}}</a>
{{end}}
</td>
<td>
{{if .MilestoneName}}
{{svg "octicon-milestone"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/milestone/{{.MilestoneID}}">{{.MilestoneName}}</a>
{{else}}
-
{{end}}
</td>
<td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
</tr>
{{end}}
</tbody>
</table>

View file

@ -0,0 +1,16 @@
<table class="ui table">
<thead>
<tr>
<th>{{ctx.Locale.Tr "repository"}}</th>
<th>{{ctx.Locale.Tr "org.worktime.time"}}</th>
</tr>
</thead>
<tbody>
{{range $.WorktimeSumResult}}
<tr>
<td>{{svg "octicon-repo"}} <a href="{{$.Org.HomeLink}}/{{PathEscape .RepoName}}/issues">{{.RepoName}}</a></td>
<td>{{svg "octicon-clock"}} {{.SumTime | Sec2Hour}}</td>
</tr>
{{end}}
</tbody>
</table>

View file

@ -9,7 +9,7 @@
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
{{.TotalTrackedTime | Sec2Hour}}
</span>
</div>
{{end}}

View file

@ -40,7 +40,7 @@
<div class="ui compact tiny secondary menu">
<span class="item" data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
{{.TotalTrackedTime | Sec2Hour}}
</span>
</div>
{{end}}

View file

@ -50,7 +50,7 @@
{{if .TotalTrackedTime}}
<div data-tooltip-content='{{ctx.Locale.Tr "tracked_time_summary"}}'>
{{svg "octicon-clock"}}
{{.TotalTrackedTime | Sec2Time}}
{{.TotalTrackedTime | Sec2Hour}}
</div>
{{end}}
</div>

View file

@ -41,7 +41,7 @@
{{if .TotalTrackedTime}}
<div class="flex-text-block">
{{svg "octicon-clock"}}
{{.TotalTrackedTime|Sec2Time}}
{{.TotalTrackedTime|Sec2Hour}}
</div>
{{end}}
{{if .UpdatedUnix}}

View file

@ -72,7 +72,7 @@
{{end}}
{{if .WorkingUsers}}
<div class="ui comments tw-mt-2">
{{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Time)}}
{{ctx.Locale.Tr "repo.issues.time_spent_from_all_authors" ($.Issue.TotalTrackedTime | Sec2Hour)}}
<div>
{{range $user, $trackedtime := .WorkingUsers}}
<div class="comment tw-mt-2">
@ -82,7 +82,7 @@
<div class="content">
{{template "shared/user/authorlink" $user}}
<div class="text">
{{$trackedtime|Sec2Time}}
{{$trackedtime|Sec2Hour}}
</div>
</div>
</div>

View file

@ -252,7 +252,7 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$timeStr := .RenderedContent}} {{/* compatibility with time comments made before v1.21 */}}
{{if not $timeStr}}{{$timeStr = .Content|Sec2Time}}{{end}}
{{if not $timeStr}}{{$timeStr = .Content|Sec2Hour}}{{end}}
{{ctx.Locale.Tr "repo.issues.stop_tracking_history" $timeStr $createdStr}}
</span>
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
@ -264,7 +264,7 @@
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$timeStr := .RenderedContent}} {{/* compatibility with time comments made before v1.21 */}}
{{if not $timeStr}}{{$timeStr = .Content|Sec2Time}}{{end}}
{{if not $timeStr}}{{$timeStr = .Content|Sec2Hour}}{{end}}
{{ctx.Locale.Tr "repo.issues.add_time_history" $timeStr $createdStr}}
</span>
{{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
@ -506,7 +506,7 @@
{{/* compatibility with time comments made before v1.21 */}}
<span class="text grey muted-links">{{.RenderedContent}}</span>
{{else}}
<span class="text grey muted-links">- {{.Content|Sec2Time}}</span>
<span class="text grey muted-links">- {{.Content|Sec2Hour}}</span>
{{end}}
</div>
</div>

View file

@ -28,7 +28,7 @@
{{if .TotalTrackedTime}}
<div class="text grey flex-text-block">
{{svg "octicon-clock" 16}}
{{.TotalTrackedTime | Sec2Time}}
{{.TotalTrackedTime | Sec2Hour}}
</div>
{{end}}
</div>

View file

@ -100,7 +100,7 @@
{{if .TotalTrackedTime}}
<div class="flex-text-block">
{{svg "octicon-clock"}}
{{.TotalTrackedTime|Sec2Time}}
{{.TotalTrackedTime|Sec2Hour}}
</div>
{{end}}
{{if .UpdatedUnix}}