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