diff --git a/src/main/scala/app/IssuesController.scala b/src/main/scala/app/IssuesController.scala index 38f617d..d543f98 100644 --- a/src/main/scala/app/IssuesController.scala +++ b/src/main/scala/app/IssuesController.scala @@ -71,7 +71,8 @@ getRepository(owner, repository, baseUrl) match { case None => NotFound() - case Some(r) => issues.html.milestones(state, getMilestones(owner, repository), r, isWritable(owner, repository, context.loginAccount)) + case Some(r) => issues.html.milestones(state, getMilestones(owner, repository), + getMilestoneIssueCounts(owner, repository), r, isWritable(owner, repository, context.loginAccount)) } }) diff --git a/src/main/scala/service/IssuesService.scala b/src/main/scala/service/IssuesService.scala index b7e6666..9946ca3 100644 --- a/src/main/scala/service/IssuesService.scala +++ b/src/main/scala/service/IssuesService.scala @@ -82,6 +82,24 @@ .sortBy(_.milestoneId desc) .firstOption + def getMilestoneIssueCounts(owner: String, repository: String): Map[(Int, Boolean), Int] = { + import scala.slick.jdbc.{GetResult, StaticQuery} + import StaticQuery.interpolation + + case class IssueCount(userName: String, repositoryName: String, milestoneId: Int, closed: Boolean, count: Int) + implicit val getIssueCount = GetResult(r => IssueCount(r.<<, r.<<, r.<<, r.<<, r.<<)) + + sql""" + select USER_NAME, REPOSITORY_NAME, MILESTONE_ID, CLOSED, COUNT(ISSUE_ID) + from ISSUE + where USER_NAME = $owner AND REPOSITORY_NAME = $repository AND MILESTONE_ID IS NOT NULL + group by USER_NAME, REPOSITORY_NAME, MILESTONE_ID, CLOSED""" + .as[IssueCount] + .list + .map { x => (x.milestoneId, x.closed) -> x.count } + .toMap + } + def getMilestones(owner: String, repository: String): List[Milestone] = Query(Milestones) .filter(m => (m.userName is owner.bind) && (m.repositoryName is repository.bind)) diff --git a/src/main/twirl/issues/milestones.scala.html b/src/main/twirl/issues/milestones.scala.html index 9fab1ea..a4ed39f 100644 --- a/src/main/twirl/issues/milestones.scala.html +++ b/src/main/twirl/issues/milestones.scala.html @@ -1,4 +1,4 @@ -@(state: String, milestones: List[model.Milestone], repository: service.RepositoryService.RepositoryInfo, isWritable: Boolean)(implicit context: app.Context) +@(state: String, milestones: List[model.Milestone], counts: Map[(Int, Boolean), Int], repository: service.RepositoryService.RepositoryInfo, isWritable: Boolean)(implicit context: app.Context) @import context._ @import view.helpers @html.main("Milestones - " + repository.owner + "/" + repository.name){ @@ -45,24 +45,37 @@ }
-
-
- @if(isWritable){ - Edit - @if(milestone.closedDate.isDefined){ - Open - } else { - Close + @defining((counts.getOrElse((milestone.milestoneId, true), 0), counts.getOrElse((milestone.milestoneId, false), 0))) { case (closedCount, openCount) => +
+ + @closedCount closed - @openCount open
- 0 closed - 0 open +
+ @if(closedCount > 0){ + + } + + @if(closedCount == 0){ + 0% + } else { + @((closedCount.toDouble / (openCount + closedCount).toDouble * 100).toInt)% + } + +
-
10%
-
+ } @if(milestone.description.isDefined){
@helpers.markdown(milestone.description.get, repository, false, false, false) @@ -93,39 +106,3 @@ }); }); - \ No newline at end of file diff --git a/src/main/webapp/assets/common/css/gitbucket.css b/src/main/webapp/assets/common/css/gitbucket.css index 0e82859..add38c0 100644 --- a/src/main/webapp/assets/common/css/gitbucket.css +++ b/src/main/webapp/assets/common/css/gitbucket.css @@ -238,6 +238,56 @@ text-decoration: underline; } +a.milestone-title { + font-size: 120%; + font-weight: bold; +} +div.milestone-description { + border-top: 1px solid #eee; + color: #666; +} + +div.milestone-menu { + font-size: 80%; +} + +div.milestone-menu a { + margin-left: 8px; + font-weight: bold; +} + +div.milestone-menu a.delete { + color: #b00; +} + +div.milestone-progress { + position: relative; + height: 20px; + color: white; + margin-bottom: 4px; + font-weight: bold; + font-size: 12px; + text-shadow: 0px 0px 5px #444; + background-color: silver; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; +} + +span.milestone-progress { + position: absolute; + height: 100%; + background-color: green; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; +} + +span.milestone-percentage { + position: absolute; + padding-left: 8px; +} + div.markdown-body table { /*width: 100%;*/ margin-bottom: 20px;