nitrpg: extends PlayerReviewsPanel to add `request_for_comments` tags
[nit.git] / contrib / nitrpg / src / templates / panels.nit
index 0a49241..1883f30 100644 (file)
@@ -17,7 +17,8 @@
 # Panels templates for `nitpg`.
 module panels
 
-import templates_base
+import templates_events
+import markdown
 
 # A panel can be displayed in a html page.
 #
@@ -98,6 +99,86 @@ class ErrorPanel
 
 end
 
+# A panel that display a markdown content rendered as HTML.
+class MDPanel
+       super Panel
+
+       # Markdown text to display.
+       var text: String
+
+       redef fun rendering do
+               add """<div class="panel">
+                           <div class="panel-body">{{{text.md_to_html}}}</div>
+                         </div>"""
+       end
+end
+
+# Display a list of active game.
+#
+# Used for NitRPG homepage.
+class GamesShortListPanel
+       super Panel
+
+       # Root url used for links.
+       var root_url: String
+
+       # List of NitRPG games to display.
+       var games: Array[Game]
+
+       redef fun render_title do
+               add "<span class=\"glyphicon glyphicon-home\"></span>&nbsp;&nbsp;"
+               add "<a href=\"{root_url}/games\">Active games</a>"
+       end
+
+       redef fun render_body do
+               if games.is_empty then
+                       add "<em>No game yet...</em>"
+                       return
+               end
+               var sorted = games.to_a
+               (new GamePlayersComparator).sort(sorted)
+               for game in sorted do
+                       add "{game.link} ({game.load_players.length} players)<br>"
+               end
+       end
+end
+
+# A panel that display a list of player in a repo.
+class GamesListPanel
+       super GamesShortListPanel
+       super TablePanel
+
+       redef fun render_title do
+               add "<span class=\"glyphicon glyphicon-home\"></span>&nbsp;&nbsp;"
+               add "<a href=\"{root_url}/games\">Active games</a>"
+       end
+
+       redef fun render_body do
+               if games.is_empty then
+                       add "<div class=\"panel-body\">"
+                       add "<em>No player yet...</em>"
+                       add "</div>"
+                       return
+               end
+               var sorted = games.to_a
+               (new GamePlayersComparator).sort(sorted)
+               add """<table class="table table-striped table-hover">
+                           <tr>
+                                <th>Game</th>
+                                <th>Players</th>
+                                <th>Achievements</th>
+                               </tr>"""
+               for game in sorted do
+                       add "<tr>"
+                       add " <td>{game.link}</td>"
+                       add " <td>{game.load_players.length}</td>"
+                       add " <td>{game.load_achievements.length}</td>"
+                       add "</tr>"
+               end
+               add "</table>"
+       end
+end
+
 # A panel that display repo statistics.
 class GameStatusPanel
        super Panel
@@ -107,12 +188,14 @@ class GameStatusPanel
 
        redef fun render_title do
                add "<span class=\"glyphicon glyphicon-home\"></span>&nbsp;&nbsp;"
-               add "<a href=\"{game.url}\">{game.name}</a>"
+               add "{game.link}"
        end
 
        redef fun render_body do
                add "<strong class=\"text-success\">{game.load_players.length}</strong>"
-               add " <a href=\"{game.url}/players\">players</a><br><br>"
+               add " <a href=\"{game.url}/players\">players</a><br>"
+               add "<strong class=\"text-success\">{game.stats["achievements"]}</strong>"
+               add " <a href=\"{game.url}/achievements\">achievements</a><br><br>"
                add "<strong class=\"text-success\">{game.stats["pulls"]}</strong> pull requests"
                add " (<strong>{game.stats["pulls_open"]}</strong> open)<br>"
                add "<strong class=\"text-success\">{game.stats["issues"]}</strong> issues"
@@ -135,8 +218,7 @@ class PlayerStatusPanel
                add "<a href=\"{player.url}\">"
                add " <img class=\"img-circle\" style=\"width: 30px\""
                add "   src=\"{player.user.avatar_url}\" alt=\"{player.name}\">"
-               add "</a>&nbsp;&nbsp;"
-               add "<a href=\"{player.url}\">{player.name}</a>"
+               add "</a>&nbsp;&nbsp;{player.link}"
        end
 
        redef fun render_body do
@@ -145,10 +227,10 @@ class PlayerStatusPanel
                add "<p class=\"lead\">ranked "
                add " <span class=\"text-success\"># {ranking[player.name]}</span></p>"
                add "<strong class=\"text-success\">{player.nitcoins}</strong> nitcoins<br><br>"
+               add "<strong class=\"text-success\">{player.stats["achievements"]}</strong> achievements<br><br>"
                add "<strong>{player.stats["pulls"]}</strong> pull requests<br>"
                add "<strong>{player.stats["issues"]}</strong> issues<br>"
                add "<strong>{player.stats["commits"]}</strong> commits"
-
        end
 end
 
@@ -172,9 +254,7 @@ class ShortListPlayersPanel
                end
                (new PlayerCoinComparator).sort(players)
                for player in players do
-                       add "<a href=\"{player.url}\">"
-                       add player.name
-                       add "</a>&nbsp;({player.nitcoins})<br>"
+                       add "{player.nitcoins} - {player.link}<br>"
                end
        end
 end
@@ -210,7 +290,7 @@ class ListPlayersPanel
                for player in players do
                        add "<tr>"
                        add " <td>{rank}</td>"
-                       add " <td><a href=\"{player.url}\">{player.name}</a></td>"
+                       add " <td>{player.link}</td>"
                        add " <td>{player.nitcoins}</td>"
                        add "</tr>"
                        rank += 1
@@ -256,7 +336,7 @@ class PodiumPanel
                                                                src="{{{player.user.avatar_url}}}" alt="{{{player.name}}}">
                                                </a>
                                        </p>
-                                       <p><a href="{{{player.url}}}">{{{player.name}}}</a></p>
+                                       <p>{{{player.link}}}</p>
                                        <p>{{{player.nitcoins}}}</p>
                                        <div class=" progress-bar-warning progress-bar-striped"
                                                style="height: {{{size}}}px;"></div>
@@ -280,31 +360,164 @@ class PlayerReviewsPanel
 
        redef fun render_title do
                add "<span class=\"glyphicon glyphicon-check\"></span>&nbsp;&nbsp;"
-               add "Review pull requests to gain nitcoins!"
+               add "Review pull requests and comment issues to gain nitcoins!"
        end
 
        redef fun render_body do
                var q = "is:open label:need_review sort:updated-asc " +
                        "-involves:{player.name}"
 
-               var issues = game.repo.search_issues(q)
+               var q2 = "is:open label:request_for_comments sort:updated-asc " +
+                       "-involves:{player.name}"
+
+               var issues = new ArraySet[Issue]
+               issues.add_all game.repo.search_issues(q).as(not null)
+               issues.add_all game.repo.search_issues(q2).as(not null)
                if issues.is_empty then
-                       add "<em>No pull request to review yet...</em>"
+                       add "<em>No pull request or issue to review yet...</em>"
                        return
                end
                for issue in issues do
+                       var user = issue.user
+                       var uplay = user.player(game)
                        add """<div class="media">
-                               <a class="media-left" href="{{{player.url}}}">
+                               <a class="media-left" href="{{{uplay.url}}}">
                                         <img class=\"img-circle\" style="width:50px"
-                                          src="{{{issue.user.avatar_url}}}" alt="{{{issue.user.login}}}">
+                                          src="{{{user.avatar_url}}}" alt="{{{uplay.name}}}">
                                        </a>
                                        <div class="media-body">
                                         <h4 class="media-heading">
-                                        <a href="{{{issue.html_url}}}">#{{{issue.number}}} {{{issue.title}}}</a></h4>
+                                               {{{issue.link}}} {{{issue.title}}}
+                                       </h4>
                                         <span class="text-muted">opened by </span>
-                                        <a href="{{{player.url}}}">{{{issue.user.login}}}</a>
+                                        {{{uplay.link}}}
                                        </div>
                                   </div>"""
                end
        end
 end
+
+# A `Panel` that displays a pagined list of events stored in the `entity`.
+#
+# This way the panel can be used to view events stored under `Game`, `Player`...
+class EventListPanel
+       super Panel
+
+       # Entity to load the events from.
+       var entity: GameEntity
+
+       # Number of events to display.
+       var limit: Int
+
+       # From where to start?
+       var from: Int
+
+       redef fun render_title do
+               add "<span class=\"glyphicon glyphicon-flash\"></span>&nbsp;&nbsp;"
+               add "Last events"
+       end
+
+       redef fun render_body do
+               var events = entity.load_events
+               if events.is_empty then
+                       add "<em>No event yet...</em>"
+                       return
+               end
+               # check input
+               if limit < 0 then limit = 10
+               if from < 0 then from = 0
+               # display events
+               for i in [from .. from + limit] do
+                       if i >= events.length then break
+                       add events[i].tpl_event.media_item
+               end
+               # pagination
+               if limit > events.length then return
+               add "<hr>"
+               add """<div class="btn-group" role="group">"""
+               if from > 0 then
+                       add """<a class="btn btn-default" role="button"
+                                       href="?pfrom={{{from - limit}}}&plimit={{{limit}}}">
+                                    <span class=\"glyphicon glyphicon-chevron-left\"></span></a>"""
+               end
+               if from + limit < events.length then
+                       add """
+                         <a class="btn btn-default" role="button"
+                                  href="?pfrom={{{from + limit}}}&plimit={{{limit}}}">
+                                   <span class=\"glyphicon glyphicon-chevron-right\"></span></a>"""
+               end
+               add "</div>"
+       end
+end
+
+# Achievement unlocked list panel.
+class AchievementsListPanel
+       super Panel
+
+       # Entity to load the events from.
+       var entity: GameEntity
+
+       redef fun render_title do
+               add "<span class=\"glyphicon glyphicon-list\"></span>&nbsp;&nbsp;"
+               add "Achievements unlocked"
+       end
+
+       redef fun render_body do
+               var achs = entity.load_achievements.values.to_a
+               if achs.is_empty then
+                       add "<em>No achievement yet...</em>"
+                       return
+               end
+               for ach in achs do add ach.list_item
+       end
+end
+
+# Achievement detail panel.
+class AchievementPanel
+       super Panel
+
+       # Achievement to display.
+       var achievement: Achievement
+
+       redef fun render_title do
+               add "<span class=\"glyphicon glyphicon-check\"></span>&nbsp;&nbsp;"
+               add "Achievement details"
+       end
+
+       redef fun render_body do
+               add """<p class=\"lead\">
+                               <span class="badge progress-bar-success"
+                                style="vertical-align: middle">+{{{achievement.reward}}}</span>
+                       {{{achievement.name}}}
+                      </p>
+                      <p><strong>{{{achievement.desc}}}</strong></p>"""
+
+               var events = achievement.load_events
+
+               if events.is_empty then
+                       add "<em>Never unlocked...</em>"
+                       return
+               end
+
+               var event = events.last
+               var tpl = event.tpl_event
+               var player = tpl.player
+               add "<hr>"
+               add """<div class="media">
+                       <a class="media-left" href="{{{player.url}}}">
+                                <span class="badge progress-bar-warning" style="position: absolute">#1</span>
+                        <img class=\"img-circle\" style="width:50px"
+                         src="{{{player.user.avatar_url}}}" alt="{{{player.name}}}">
+                       </a>
+                               <div class="media-body">
+                                <h4 class="media-heading">Unlocked first by {{{player.link}}}</h4>
+                                <span class="text-muted">at {{{event.time}}} </span>
+                               </div>
+                          </div>"""
+
+               if events.length > 1 then
+                       add """<p><br>Also unlocked by <strong class="text-success">
+                       {{{events.length}}} players</strong>.</p>"""
+               end
+       end
+end