From 45f9acebd289a81f5e926bd5ca505d68f2a4abd7 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 10 Mar 2015 15:46:31 +0100 Subject: [PATCH] SONAR-6285 add a home page for global issues --- .../src/main/coffee/issues/controller.coffee | 22 +++ .../src/main/coffee/issues/layout.coffee | 9 ++ .../src/main/coffee/issues/router.coffee | 9 +- .../coffee/issues/workspace-home-view.coffee | 146 ++++++++++++++++++ .../src/main/hbs/issues/issues-layout.hbs | 1 + .../main/hbs/issues/issues-workspace-home.hbs | 77 +++++++++ .../src/main/js/graphics/barchart.js | 4 +- .../src/main/less/components/columns.less | 9 ++ .../sonar-web/src/main/less/init/icons.less | 5 + .../sonar-web/src/main/less/pages/issues.less | 20 ++- .../resources/org/sonar/l10n/core.properties | 7 + 11 files changed, 303 insertions(+), 6 deletions(-) create mode 100644 server/sonar-web/src/main/coffee/issues/workspace-home-view.coffee create mode 100644 server/sonar-web/src/main/hbs/issues/issues-workspace-home.hbs diff --git a/server/sonar-web/src/main/coffee/issues/controller.coffee b/server/sonar-web/src/main/coffee/issues/controller.coffee index d30e8c7978c..99f39a32afe 100644 --- a/server/sonar-web/src/main/coffee/issues/controller.coffee +++ b/server/sonar-web/src/main/coffee/issues/controller.coffee @@ -22,10 +22,12 @@ define [ 'components/navigator/controller' 'issues/component-viewer/main' + 'issues/workspace-home-view' ], ( Controller ComponentViewer + HomeView ) -> $ = jQuery @@ -61,6 +63,7 @@ define [ fetchList: (firstPage = true) -> if firstPage @options.app.state.set { selectedIndex: 0, page: 1 }, { silent: true } + @hideHomePage() @closeComponentViewer() data = @_issuesParameters() @@ -201,3 +204,22 @@ define [ @options.app.issuesView.bindScrollEvents() @options.app.issuesView.scrollTo() + + showHomePage: -> + @fetchList() + @options.app.layout.workspaceComponentViewerRegion.reset() + key.setScope 'home' + @options.app.issuesView.unbindScrollEvents() + @options.app.homeView = new HomeView app: @options.app + @options.app.layout.workspaceHomeRegion.show @options.app.homeView + @options.app.layout.showHomePage() + + + hideHomePage: -> + @options.app.layout.workspaceComponentViewerRegion.reset() + @options.app.layout.workspaceHomeRegion.reset() + key.setScope 'list' + @options.app.layout.hideHomePage() + @options.app.issuesView.bindScrollEvents() + @options.app.issuesView.scrollTo() + diff --git a/server/sonar-web/src/main/coffee/issues/layout.coffee b/server/sonar-web/src/main/coffee/issues/layout.coffee index 9cef9f326dc..e06daa35e74 100644 --- a/server/sonar-web/src/main/coffee/issues/layout.coffee +++ b/server/sonar-web/src/main/coffee/issues/layout.coffee @@ -35,6 +35,7 @@ define [ workspaceHeaderRegion: '.search-navigator-workspace-header' workspaceListRegion: '.search-navigator-workspace-list' workspaceComponentViewerRegion: '.issues-workspace-component-viewer' + workspaceHomeRegion: '.issues-workspace-home' onRender: -> @@ -58,3 +59,11 @@ define [ hideComponentViewer: -> $('.issues').removeClass 'issues-extended-view' $(window).scrollTop @scroll if @scroll? + + + showHomePage: -> + $('.issues').addClass 'issues-home-view' + + + hideHomePage: -> + $('.issues').removeClass 'issues-home-view' diff --git a/server/sonar-web/src/main/coffee/issues/router.coffee b/server/sonar-web/src/main/coffee/issues/router.coffee index 4091d9c8dec..ca220549f1f 100644 --- a/server/sonar-web/src/main/coffee/issues/router.coffee +++ b/server/sonar-web/src/main/coffee/issues/router.coffee @@ -26,7 +26,7 @@ define [ class extends Router routes: - '': 'emptyQuery' + '': 'home' ':query': 'index' @@ -35,8 +35,11 @@ define [ @listenTo options.app.state, 'change:filter', @updateRoute - emptyQuery: -> - @navigate 'resolved=false', { trigger: true, replace: true } + home: -> + if @options.app.state.get 'isContext' + @navigate 'resolved=false', { trigger: true, replace: true } + else + @options.app.controller.showHomePage() index: (query) -> diff --git a/server/sonar-web/src/main/coffee/issues/workspace-home-view.coffee b/server/sonar-web/src/main/coffee/issues/workspace-home-view.coffee new file mode 100644 index 00000000000..704dbb28834 --- /dev/null +++ b/server/sonar-web/src/main/coffee/issues/workspace-home-view.coffee @@ -0,0 +1,146 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# SonarQube is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +define [ + 'widgets/issue-filter' + 'templates/issues' +], (IssueFilter) -> + + $ = jQuery + + + Handlebars.registerHelper 'issuesHomeLink', (property, value) -> + "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|#{property}=#{encodeURIComponent value}" + + Handlebars.registerHelper 'myIssuesHomeLink', (property, value) -> + "#{baseUrl}/issues/search#resolved=false|createdInLast=1w|assignees=__me__|#{property}=#{encodeURIComponent value}" + + Handlebars.registerHelper 'issueFilterHomeLink', (id) -> + "#{baseUrl}/issues/search#id=#{id}" + + + class extends Marionette.ItemView + template: Templates['issues-workspace-home'] + + + modelEvents: + 'change': 'render' + + + events: + 'click .js-barchart rect': 'selectBar' + 'click .js-my-barchart rect': 'selectMyBar' + + + initialize: -> + @model = new Backbone.Model + @requestIssues() + @requestMyIssues() + + + _getProjects: (r) -> + projectFacet = _.findWhere r.facets, property: 'projectUuids' + values = _.head projectFacet.values, 3 + values.forEach (v) => + project = _.findWhere r.projects, uuid: v.val + v.label = project.longName + values + + + _getAuthors: (r) -> + authorFacet = _.findWhere r.facets, property: 'authors' + values = _.head authorFacet.values, 3 + values + + + _getTags: (r) -> + MIN_SIZE = 10 + MAX_SIZE = 24 + tagFacet = _.findWhere r.facets, property: 'tags' + values = _.head tagFacet.values, 10 + minCount = _.min(values, (v) -> v.count).count + maxCount = _.max(values, (v) -> v.count).count + scale = d3.scale.linear().domain([minCount, maxCount]).range([MIN_SIZE, MAX_SIZE]) + values.forEach (v) => + v.size = scale v.count + values + + + requestIssues: -> + url = "#{baseUrl}/api/issues/search" + options = + resolved: false + createdInLast: '1w' + ps: 1 + facets: 'createdAt,projectUuids,authors,tags' + $.get(url, options).done (r) => + @model.set + createdAt: _.findWhere(r.facets, property: 'createdAt').values + projects: @_getProjects r + authors: @_getAuthors r + tags: @_getTags r + + + requestMyIssues: -> + url = "#{baseUrl}/api/issues/search" + options = + resolved: false + createdInLast: '1w' + assignees: '__me__' + ps: 1 + facets: 'createdAt,projectUuids,authors,tags' + $.get(url, options).done (r) => + @model.set + myCreatedAt: _.findWhere(r.facets, property: 'createdAt').values + myProjects: @_getProjects r + myTags: @_getTags r + + + onRender: -> + values = @model.get 'createdAt' + myValues = @model.get 'myCreatedAt' + @$('.js-barchart').barchart values if values? + @$('.js-my-barchart').barchart myValues if myValues? + @$('[data-toggle="tooltip"]').tooltip container: 'body' + + + selectBar: (e) -> + periodStart = $(e.currentTarget).data 'period-start' + periodEnd = $(e.currentTarget).data 'period-end' + @options.app.state.setQuery + resolved: false + createdAfter: periodStart + createdBefore: periodEnd + + + selectMyBar: (e) -> + periodStart = $(e.currentTarget).data 'period-start' + periodEnd = $(e.currentTarget).data 'period-end' + @options.app.state.setQuery + resolved: false + assignees: '__me__' + createdAfter: periodStart + createdBefore: periodEnd + + + serializeData: -> + _.extend super, + user: window.SS.user + filters: _.sortBy @options.app.filters.toJSON(), 'name' diff --git a/server/sonar-web/src/main/hbs/issues/issues-layout.hbs b/server/sonar-web/src/main/hbs/issues/issues-layout.hbs index f60fecfb03f..5a61d1f66bf 100644 --- a/server/sonar-web/src/main/hbs/issues/issues-layout.hbs +++ b/server/sonar-web/src/main/hbs/issues/issues-layout.hbs @@ -7,4 +7,5 @@
+
diff --git a/server/sonar-web/src/main/hbs/issues/issues-workspace-home.hbs b/server/sonar-web/src/main/hbs/issues/issues-workspace-home.hbs new file mode 100644 index 00000000000..3889dcdd978 --- /dev/null +++ b/server/sonar-web/src/main/hbs/issues/issues-workspace-home.hbs @@ -0,0 +1,77 @@ +
+
+
+

{{t 'issues.home.recent_issues'}}

+

({{t 'issues.home.over_last_week'}})

+ +
+

{{t 'issues.home.projects'}}

+ + {{#each projects}} + + + + + {{/each}} +
{{qualifierIcon 'TRK'}} {{label}}+{{numberShort count}}
+

{{t 'issues.home.authors'}}

+ + {{#each authors}} + + + + + {{/each}} +
{{val}}+{{numberShort count}}
+

{{t 'issues.home.tags'}}

+
    + {{#each tags}} +
  • {{val}}
  • + {{/each}} +
+
+ + {{#if user}} +
+

{{t 'issues.home.my_recent_issues'}}

+

({{t 'issues.home.over_last_week'}})

+ +
+ {{#notEmpty myProjects}} +

{{t 'issues.home.projects'}}

+ + {{#each myProjects}} + + + + + {{/each}} +
{{qualifierIcon 'TRK'}} {{label}}+{{numberShort count}}
+ {{/notEmpty}} + {{#notEmpty myTags}} +

{{t 'issues.home.tags'}}

+
    + {{#each myTags}} +
  • {{val}}
  • + {{/each}} +
+ {{/notEmpty}} + {{#notEmpty filters}} +

+ {{t 'issues.home.my_filters'}}

+
    + {{#each filters}} +
  • + {{name}} +
  • + {{/each}} +
+ {{/notEmpty}} +
+ {{/if}} +
+
diff --git a/server/sonar-web/src/main/js/graphics/barchart.js b/server/sonar-web/src/main/js/graphics/barchart.js index e8bd3b7e353..c107a68661b 100644 --- a/server/sonar-web/src/main/js/graphics/barchart.js +++ b/server/sonar-web/src/main/js/graphics/barchart.js @@ -48,7 +48,7 @@ $(this).each(function () { var options = _.defaults($(this).data(), defaults()); _.extend(options, { - width: $(this).width(), + width: options.width || $(this).width(), endDate: moment(options.endDate) }); @@ -116,7 +116,7 @@ isSameDay = ending.diff(beginning, 'days') <= 1; return d.count + '
' + beginning.format('LL') + (isSameDay ? '' : (' – ' + ending.format('LL'))); }) - .attr('data-placement', 'right') + .attr('data-placement', 'bottom') .attr('data-toggle', 'tooltip'); var maxValue = d3.max(data, function (d) { diff --git a/server/sonar-web/src/main/less/components/columns.less b/server/sonar-web/src/main/less/components/columns.less index a69f5e25d54..0e4b489e74b 100644 --- a/server/sonar-web/src/main/less/components/columns.less +++ b/server/sonar-web/src/main/less/components/columns.less @@ -31,4 +31,13 @@ width: 50%; padding: 0 10px; .box-sizing(border-box); + + &.column-one { margin: 0 25%; } +} + +.column-third { + float: left; + width: 33%; + padding: 0 10px; + .box-sizing(border-box); } diff --git a/server/sonar-web/src/main/less/init/icons.less b/server/sonar-web/src/main/less/init/icons.less index f2b19caefdd..2c1a113474c 100644 --- a/server/sonar-web/src/main/less/init/icons.less +++ b/server/sonar-web/src/main/less/init/icons.less @@ -581,6 +581,11 @@ a[class^="icon-"], a[class*=" icon-"] { content: "\f068"; font-size: @iconSmallFontSize; } +.icon-issues { + display: inline-block; + .square(60px); + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB3aWR0aD0iNjBweCIgaGVpZ2h0PSI2MHB4IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MS40MTQyMTsiPiAgICA8ZyBpZD0iTGF5ZXJfMSI+ICAgICAgICA8Zz4gICAgICAgICAgICA8cGF0aCBkPSJNMzAsMS43NzYzNmUtMTVDNDYuNTY4NSwxLjc3NjM2ZS0xNSA2MCwxMy40MzE1IDYwLDMwQzYwLDQ2LjU2ODUgNDYuNTY4NSw2MCAzMCw2MEMxMy40MzE1LDYwIDAsNDYuNTY4NSAwLDMwQzAsMTMuNDMxNSAxMy40MzE1LDEuNzc2MzZlLTE1IDMwLDEuNzc2MzZlLTE1WiIgc3R5bGU9ImZpbGw6dXJsKCN0YWcxKTsiLz4gICAgICAgICAgICA8cGF0aCBkPSJNNDgsNkM0Mi45ODY0LDIuMjMzMiAzNi43NTQyLDEuNzc2MzZlLTE1IDMwLDEuNzc2MzZlLTE1QzIzLjI0NTgsMS43NzYzNmUtMTUgMTcuMDE0MiwyLjIzMzIgMTIsNkwxMiw1NC4wMDA2QzE3LjAxNDIsNTcuNzY2OCAyMy4yNDU4LDYwIDMwLDYwQzM2Ljc1NDIsNjAgNDIuOTg2NCw1Ny43NjY4IDQ4LDU0LjAwMDZaIiBzdHlsZT0iZmlsbDpyZ2IoMjM0LDIzNCwyMzQpO2ZpbGwtcnVsZTpub256ZXJvOyIvPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00Miw5QzQyLDkuNDk1IDQxLjQ2LDkuOSA0MC44LDkuOUwxOS4yLDkuOUMxOC41NCw5LjkgMTgsOS40OTUgMTgsOUMxOCw4LjUwNSAxOC41NCw4LjEgMTkuMiw4LjFMNDAuOCw4LjFDNDEuNDYsOC4xIDQyLDguNTA1IDQyLDlaIiBzdHlsZT0iZmlsbDpyZ2IoMTMyLDEzMiwxMzIpO2ZpbGwtcnVsZTpub256ZXJvOyIvPiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yNy42LDE1QzI3LjYsMTUuNDk1IDI3LjA2LDE1LjkgMjYuNCwxNS45TDE5LjIsMTUuOUMxOC41NCwxNS45IDE4LDE1LjQ5NSAxOCwxNUMxOCwxNC41MDUgMTguNTQsMTQuMSAxOS4yLDE0LjFMMjYuNCwxNC4xQzI3LjA2LDE0LjEgMjcuNiwxNC41MDUgMjcuNiwxNVoiIHN0eWxlPSJmaWxsOnJnYigxMzIsMTMyLDEzMik7ZmlsbC1ydWxlOm5vbnplcm87Ii8+ICAgICAgICAgICAgPHBhdGggZD0iTTQyLDIxQzQyLDIxLjQ5NSA0MS40NiwyMS45IDQwLjgsMjEuOUwxOS4yLDIxLjlDMTguNTQsMjEuOSAxOCwyMS40OTUgMTgsMjFDMTgsMjAuNTA1IDE4LjU0LDIwLjEgMTkuMiwyMC4xTDQwLjgsMjAuMUM0MS40NiwyMC4xIDQyLDIwLjUwNSA0MiwyMVoiIHN0eWxlPSJmaWxsOnJnYigxMzIsMTMyLDEzMik7ZmlsbC1ydWxlOm5vbnplcm87Ii8+ICAgICAgICAgICAgPGc+ICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xOS4yLDM0LjJDMTguODY5NCwzNC4yIDE4LjYsMzMuOTMxMiAxOC42LDMzLjZMMTguNiwyN0MxOC42LDI2LjY2OTQgMTguODY5NCwyNi40IDE5LjIsMjYuNEw0MC44LDI2LjRDNDEuMTMxMiwyNi40IDQxLjQsMjYuNjY5NCA0MS40LDI3TDQxLjQsMzMuNkM0MS40LDMzLjkzMTIgNDEuMTMxMiwzNC4yIDQwLjgsMzQuMloiIHN0eWxlPSJmaWxsOnJnYigyNTMsMjM0LDIzNCk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+ICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik00MC44LDI3TDQwLjgsMzMuNkwxOS4yLDMzLjZMMTkuMiwyN000MC44LDI1LjhMMTkuMiwyNS44QzE4LjU0LDI1LjggMTgsMjYuMzQgMTgsMjdMMTgsMzMuNkMxOCwzNC4yNiAxOC41NCwzNC44IDE5LjIsMzQuOEw0MC44LDM0LjhDNDEuNDYsMzQuOCA0MiwzNC4yNiA0MiwzMy42TDQyLDI3QzQyLDI2LjM0IDQxLjQ2LDI1LjggNDAuOCwyNS44WiIgc3R5bGU9ImZpbGw6cmdiKDIyMSw2NCw2NCk7ZmlsbC1ydWxlOm5vbnplcm87Ii8+ICAgICAgICAgICAgPC9nPiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yNy42LDM5QzI3LjYsMzkuNDk1IDI3LjA2LDM5LjkgMjYuNCwzOS45TDE5LjIsMzkuOUMxOC41NCwzOS45IDE4LDM5LjQ5NSAxOCwzOUMxOCwzOC41MDUgMTguNTQsMzguMSAxOS4yLDM4LjFMMjYuNCwzOC4xQzI3LjA2LDM4LjEgMjcuNiwzOC41MDUgMjcuNiwzOVoiIHN0eWxlPSJmaWxsOnJnYigxMzIsMTMyLDEzMik7ZmlsbC1ydWxlOm5vbnplcm87Ii8+ICAgICAgICAgICAgPHBhdGggZD0iTTM3LjIsNDVDMzcuMiw0NS40OTUgMzYuNjYsNDUuOSAzNiw0NS45TDE5LjIsNDUuOUMxOC41NCw0NS45IDE4LDQ1LjQ5NSAxOCw0NUMxOCw0NC41MDUgMTguNTQsNDQuMSAxOS4yLDQ0LjFMMzYsNDQuMUMzNi42Niw0NC4xIDM3LjIsNDQuNTA1IDM3LjIsNDVaIiBzdHlsZT0iZmlsbDpyZ2IoMTMyLDEzMiwxMzIpO2ZpbGwtcnVsZTpub256ZXJvOyIvPiAgICAgICAgICAgIDxwYXRoIGQ9Ik00Miw1MUM0Miw1MS40OTUgNDEuNDYsNTEuOSA0MC44LDUxLjlMMTkuMiw1MS45QzE4LjU0LDUxLjkgMTgsNTEuNDk1IDE4LDUxQzE4LDUwLjUwNSAxOC41NCw1MC4xIDE5LjIsNTAuMUw0MC44LDUwLjFDNDEuNDYsNTAuMSA0Miw1MC41MDUgNDIsNTFaIiBzdHlsZT0iZmlsbDpyZ2IoMTMyLDEzMiwxMzIpO2ZpbGwtcnVsZTpub256ZXJvOyIvPiAgICAgICAgPC9nPiAgICA8L2c+ICAgIDxkZWZzPiAgICAgICAgPGxpbmVhckdyYWRpZW50IGlkPSJ0YWcxIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMy42NzM5NmUtMTUsNjAuMDAwMywtNjAuMDAwMywzLjY3Mzk2ZS0xNSwyOS45OTk3LDEuNzc2MzZlLTE1KSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6cmdiKDIyOSwyMDksMzQpO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdHlsZT0ic3RvcC1jb2xvcjpyZ2IoMjE1LDE5NywzMyk7c3RvcC1vcGFjaXR5OjEiLz48L2xpbmVhckdyYWRpZW50PiAgICA8L2RlZnM+PC9zdmc+); +} /* diff --git a/server/sonar-web/src/main/less/pages/issues.less b/server/sonar-web/src/main/less/pages/issues.less index 05c13174fe4..0cc944a4c37 100644 --- a/server/sonar-web/src/main/less/pages/issues.less +++ b/server/sonar-web/src/main/less/pages/issues.less @@ -29,7 +29,8 @@ &.sticky { .issues-workspace-list, - .issues-workspace-component-viewer { + .issues-workspace-component-viewer, + .issues-workspace-home { padding-top: 22px + 5px + 5px + 1px + 10px; } @@ -153,3 +154,20 @@ background-size: 4px; background-position: bottom; } + +.issues-home-view { + .search-navigator-workspace-list, + .issues-workspace-component-viewer { + display: none; + } +} + +.issues-workspace-home { + width: ~"calc(100vw - @{sideWidth})"; + max-width: 900px; + margin-left: auto; + margin-right: auto; + padding-left: 10px; + padding-right: 10px; + .box-sizing(border-box); +} diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 9e3150b7d82..66a429af3b1 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -719,6 +719,13 @@ issue.technical_debt_deleted=Rule not configured to generate technical debt esti issue.creation_date=Created issues.return_to_list=Return to List issues.issues_limit_reached=For usability reasons, only the {0} issues are displayed. +issues.home.recent_issues=Recent Issues +issues.home.my_recent_issues=My Recent Issues +issues.home.over_last_week=Over Last Week +issues.home.my_filters=My Filters +issues.home.projects=Projects +issues.home.authors=Authors +issues.home.tags=Tags #------------------------------------------------------------------------------ -- 2.39.5