contrib/nitrpg: add MDPanel to display MDContent
[nit.git] / contrib / nitrpg / src / templates / panels.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014-2015 Alexandre Terrasa <alexandre@moz-code.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Panels templates for `nitpg`.
18 module panels
19
20 import templates_events
21 import markdown
22
23 # A panel can be displayed in a html page.
24 #
25 # This display a Bootstrap panel.
26 class Panel
27 super Template
28
29 redef fun rendering do
30 add """<div class="panel panel-default">
31 <div class="panel-heading">
32 <h3 class="panel-title">"""
33 render_title
34 add """ </h3>
35 </div>
36 <div class="panel-body">"""
37 render_body
38 add """</div>
39 </div>"""
40 end
41
42 # Render the panel title.
43 # Betweem `<h4>` tags.
44 fun render_title do end
45
46 # Render the panel body.
47 fun render_body do end
48 end
49
50 # A panel that contain only a table as body.
51 class TablePanel
52 super Panel
53
54 redef fun rendering do
55 add """<div class="panel panel-default">
56 <div class="panel-heading">
57 <h3 class="panel-title">"""
58 render_title
59 add """
60 </h3>
61 </div>"""
62 render_body
63 add """</div>"""
64 end
65 end
66
67 # Display an error message within a panel.
68 class ErrorPanel
69 super Panel
70
71 redef fun rendering do
72 add """
73 <div class="panel panel-danger">
74 <div class="panel-heading">
75 <h3 class="panel-title">"""
76 render_title
77 add """
78 </h3>
79 </div>
80 <div class="panel-body">"""
81 render_body
82 add """
83 </div>
84 </div>
85 """
86 end
87
88 # The error message to display as panel body.
89 var msg: String
90
91 redef fun render_title do
92 add "<span class=\"glyphicon glyphicon-warning-sign\"></span>&nbsp;&nbsp;"
93 add "Error"
94 end
95
96 redef fun render_body do
97 add msg.html_escape
98 end
99
100 end
101
102 # A panel that display a markdown content rendered as HTML.
103 class MDPanel
104 super Panel
105
106 # Markdown text to display.
107 var text: String
108
109 redef fun rendering do
110 add """<div class="panel">
111 <div class="panel-body">{{{text.md_to_html}}}</div>
112 </div>"""
113 end
114 end
115
116 # A panel that display repo statistics.
117 class GameStatusPanel
118 super Panel
119
120 # Repo to display.
121 var game: Game
122
123 redef fun render_title do
124 add "<span class=\"glyphicon glyphicon-home\"></span>&nbsp;&nbsp;"
125 add "{game.link}"
126 end
127
128 redef fun render_body do
129 add "<strong class=\"text-success\">{game.load_players.length}</strong>"
130 add " <a href=\"{game.url}/players\">players</a><br>"
131 add "<strong class=\"text-success\">{game.stats["achievements"]}</strong>"
132 add " <a href=\"{game.url}/achievements\">achievements</a><br><br>"
133 add "<strong class=\"text-success\">{game.stats["pulls"]}</strong> pull requests"
134 add " (<strong>{game.stats["pulls_open"]}</strong> open)<br>"
135 add "<strong class=\"text-success\">{game.stats["issues"]}</strong> issues"
136 add " (<strong>{game.stats["issues_open"]}</strong> open)<br>"
137 add "<strong class=\"text-success\">{game.stats["commits"]}</strong> commits"
138 end
139 end
140
141 # Player status panel.
142 class PlayerStatusPanel
143 super Panel
144
145 # Game instance.
146 var game: Game
147
148 # Target player.
149 var player: Player
150
151 redef fun render_title do
152 add "<a href=\"{player.url}\">"
153 add " <img class=\"img-circle\" style=\"width: 30px\""
154 add " src=\"{player.user.avatar_url}\" alt=\"{player.name}\">"
155 add "</a>&nbsp;&nbsp;{player.link}"
156 end
157
158 redef fun render_body do
159 var ranking = game.player_ranking
160 # TODO player.rank
161 add "<p class=\"lead\">ranked "
162 add " <span class=\"text-success\"># {ranking[player.name]}</span></p>"
163 add "<strong class=\"text-success\">{player.nitcoins}</strong> nitcoins<br><br>"
164 add "<strong class=\"text-success\">{player.stats["achievements"]}</strong> achievements<br><br>"
165 add "<strong>{player.stats["pulls"]}</strong> pull requests<br>"
166 add "<strong>{player.stats["issues"]}</strong> issues<br>"
167 add "<strong>{player.stats["commits"]}</strong> commits"
168 end
169 end
170
171 # A panel that display a list of player in a repo.
172 class ShortListPlayersPanel
173 super Panel
174
175 # Game instance.
176 var game: Game
177
178 redef fun render_title do
179 add "<span class=\"glyphicon glyphicon-user\"></span>&nbsp;&nbsp;"
180 add "<a href=\"{game.url}/players\">Players</a>"
181 end
182
183 redef fun render_body do
184 var players = game.load_players.values.to_a
185 if players.is_empty then
186 add "<em>No player yet...</em>"
187 return
188 end
189 (new PlayerCoinComparator).sort(players)
190 for player in players do
191 add "{player.nitcoins} - {player.link}<br>"
192 end
193 end
194 end
195
196 # A panel that display a list of player in a repo.
197 class ListPlayersPanel
198 super TablePanel
199
200 # Game instance.
201 var game: Game
202
203 redef fun render_title do
204 add "<span class=\"glyphicon glyphicon-user\"></span>&nbsp;&nbsp;"
205 add "<a href=\"{game.url}/players\">Players</a>"
206 end
207
208 redef fun render_body do
209 var players = game.load_players.values.to_a
210 (new PlayerCoinComparator).sort(players)
211 if players.is_empty then
212 add "<div class=\"panel-body\">"
213 add "<em>No player yet...</em>"
214 add "</div>"
215 return
216 end
217 add """<table class="table table-striped table-hover">
218 <tr>
219 <th>#</th>
220 <th>Player</th>
221 <th>Nitcoins</th>
222 </tr>"""
223 var rank = 1
224 for player in players do
225 add "<tr>"
226 add " <td>{rank}</td>"
227 add " <td>{player.link}</td>"
228 add " <td>{player.nitcoins}</td>"
229 add "</tr>"
230 rank += 1
231 end
232 add "</table>"
233 end
234 end
235
236 # A panel that display the podium.
237 class PodiumPanel
238 super Panel
239
240 # Game instance.
241 var game: Game
242
243 redef fun render_title do
244 add "<span class=\"glyphicon glyphicon-stats\"></span>&nbsp;&nbsp;Hall of fame"
245 end
246
247 redef fun render_body do
248 var players = game.load_players.values.to_a
249 (new PlayerCoinComparator).sort(players)
250 if players.is_empty then
251 add "<em>No players yet...</em>"
252 return
253 end
254 add """
255 <div class="container-fluid">
256 <div id="podium" class="row row-sm-height">"""
257 var max = players.first.nitcoins
258 var orders = [3, 1, 0, 2, 4]
259 for order in orders do
260 if order >= players.length then continue
261 var player = players[order]
262 var size = 0
263 if max > 0 then size = player.nitcoins * 300 / max
264 add """
265 <div class="col-xs-2 col-xs-height col-xs-offset-{{{order}}} col-bottom"
266 style="text-align: center;">
267 <p>
268 <a href="{{{player.url}}}">
269 <img class="img-circle" style="width: 80px"
270 src="{{{player.user.avatar_url}}}" alt="{{{player.name}}}">
271 </a>
272 </p>
273 <p>{{{player.link}}}</p>
274 <p>{{{player.nitcoins}}}</p>
275 <div class=" progress-bar-warning progress-bar-striped"
276 style="height: {{{size}}}px;"></div>
277 </div>"""
278 end
279 add """
280 </div>
281 </div>"""
282 end
283 end
284
285 # A `Panel` that displays the list of PR to review for a `Player`.
286 class PlayerReviewsPanel
287 super Panel
288
289 # Repo to display.
290 var game: Game
291
292 # Player to display customized list for.
293 var player: Player
294
295 redef fun render_title do
296 add "<span class=\"glyphicon glyphicon-check\"></span>&nbsp;&nbsp;"
297 add "Review pull requests to gain nitcoins!"
298 end
299
300 redef fun render_body do
301 var q = "is:open label:need_review sort:updated-asc " +
302 "-involves:{player.name}"
303
304 var issues = game.repo.search_issues(q)
305 if issues.is_empty then
306 add "<em>No pull request to review yet...</em>"
307 return
308 end
309 for issue in issues do
310 var user = issue.user
311 var uplay = user.player(game)
312 add """<div class="media">
313 <a class="media-left" href="{{{uplay.url}}}">
314 <img class=\"img-circle\" style="width:50px"
315 src="{{{user.avatar_url}}}" alt="{{{uplay.name}}}">
316 </a>
317 <div class="media-body">
318 <h4 class="media-heading">
319 {{{issue.link}}} {{{issue.title}}}
320 </h4>
321 <span class="text-muted">opened by </span>
322 {{{uplay.link}}}
323 </div>
324 </div>"""
325 end
326 end
327 end
328
329 # A `Panel` that displays a pagined list of events stored in the `entity`.
330 #
331 # This way the panel can be used to view events stored under `Game`, `Player`...
332 class EventListPanel
333 super Panel
334
335 # Entity to load the events from.
336 var entity: GameEntity
337
338 # Number of events to display.
339 var limit: Int
340
341 # From where to start?
342 var from: Int
343
344 redef fun render_title do
345 add "<span class=\"glyphicon glyphicon-flash\"></span>&nbsp;&nbsp;"
346 add "Last events"
347 end
348
349 redef fun render_body do
350 var events = entity.load_events
351 if events.is_empty then
352 add "<em>No event yet...</em>"
353 return
354 end
355 # check input
356 if limit < 0 then limit = 10
357 if from < 0 then from = 0
358 # display events
359 for i in [from .. from + limit] do
360 if i >= events.length then break
361 add events[i].tpl_event.media_item
362 end
363 # pagination
364 if limit > events.length then return
365 add "<hr>"
366 add """<div class="btn-group" role="group">"""
367 if from > 0 then
368 add """<a class="btn btn-default" role="button"
369 href="?pfrom={{{from - limit}}}&plimit={{{limit}}}">
370 <span class=\"glyphicon glyphicon-chevron-left\"></span></a>"""
371 end
372 if from + limit < events.length then
373 add """
374 <a class="btn btn-default" role="button"
375 href="?pfrom={{{from + limit}}}&plimit={{{limit}}}">
376 <span class=\"glyphicon glyphicon-chevron-right\"></span></a>"""
377 end
378 add "</div>"
379 end
380 end
381
382 # Achievement unlocked list panel.
383 class AchievementsListPanel
384 super Panel
385
386 # Entity to load the events from.
387 var entity: GameEntity
388
389 redef fun render_title do
390 add "<span class=\"glyphicon glyphicon-list\"></span>&nbsp;&nbsp;"
391 add "Achievements unlocked"
392 end
393
394 redef fun render_body do
395 var achs = entity.load_achievements.values.to_a
396 if achs.is_empty then
397 add "<em>No achievement yet...</em>"
398 return
399 end
400 for ach in achs do add ach.list_item
401 end
402 end
403
404 # Achievement detail panel.
405 class AchievementPanel
406 super Panel
407
408 # Achievement to display.
409 var achievement: Achievement
410
411 redef fun render_title do
412 add "<span class=\"glyphicon glyphicon-check\"></span>&nbsp;&nbsp;"
413 add "Achievement details"
414 end
415
416 redef fun render_body do
417 add """<p class=\"lead\">
418 <span class="badge progress-bar-success"
419 style="vertical-align: middle">+{{{achievement.reward}}}</span>
420 {{{achievement.name}}}
421 </p>
422 <p><strong>{{{achievement.desc}}}</strong></p>"""
423
424 var events = achievement.load_events
425
426 if events.is_empty then
427 add "<em>Never unlocked...</em>"
428 return
429 end
430
431 var event = events.last
432 var tpl = event.tpl_event
433 var player = tpl.player
434 add "<hr>"
435 add """<div class="media">
436 <a class="media-left" href="{{{player.url}}}">
437 <span class="badge progress-bar-warning" style="position: absolute">#1</span>
438 <img class=\"img-circle\" style="width:50px"
439 src="{{{player.user.avatar_url}}}" alt="{{{player.name}}}">
440 </a>
441 <div class="media-body">
442 <h4 class="media-heading">Unlocked first by {{{player.link}}}</h4>
443 <span class="text-muted">at {{{event.time}}} </span>
444 </div>
445 </div>"""
446
447 if events.length > 1 then
448 add """<p><br>Also unlocked by <strong class="text-success">
449 {{{events.length}}} players</strong>.</p>"""
450 end
451 end
452 end