dashboard: perfect view
[fic/server.git] / dashboard / static / index.html
1 <!DOCTYPE html>
2 <html ng-app="FICApp">
3   <head>
4     <meta charset="utf-8">
5     <title>Challenge Forensic</title>
6     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7     <meta http-equiv="X-UA-Compatible" content="IE=edge">
8     <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
9     <meta name="author" content="EPITA Laboratoire SRS">
10     <meta name="robots" content="all">
11     <base href="/">
12     <link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="screen">
13     <link href="/css/glyphicon.css" type="text/css" rel="stylesheet" media="screen">
14     <link href="/css/fic.css" type="text/css" rel="stylesheet" media="screen">
15     <script src="/js/angular.min.js"></script>
16   </head>
17   <body class="bg-light bg-public" style="overflow: hidden;" class="container-fluid" ng-controller="DataController">
18     <div class="row" style="margin:10px 0">
19       <div class="col-8">
20         <noscript>
21           <div class="alert alert-danger">
22             <strong>Veuillez activer le JavaScript.</strong> Ce site requiert un navigateur interprêtant le JavaScript pour fonctionner. Veuillez l'activer ou en télécharger un supportant cette technologie.
23           </div>
24         </noscript>
25
26         <div ng-repeat="(k,s) in scene" class="repeatedd-item" style="margin-bottom: 15px;" ng-cloak>
27
28           <div class="card niceborder bg-dark" ng-if="s.type == 'welcome' && !s.params.hide && s.params.kind == 'teams'">
29             <div class="card-body text-light text-indent">
30               <h1 ng-if="!s.params.notitle">Bienvenue au challenge forensic&nbsp;!</h1>
31               <p class="lead text-justify">
32                 Avant de vous installer, venez récupérer votre clef USB auprès
33                 de notre équipe. Elle contient le certificat qui vous permettra
34                 de vous authentifier auprès de notre serveur.
35               </p>
36               <p class="lead text-justify">
37                 Une fois connecté au réseau, contactez notre serveur sur&nbsp;:
38                 <span style="display: block; font-size: 200%; text-indent: 0;" class="text-center">
39                   <a style="font-family: mono" href="https://fic.srs.epita.fr/"><span class="text-info">https://fic.srs.epita.fr/</span></a>
40                 </span>
41               </p>
42             </div>
43           </div>
44
45           <div class="card border-{{s.params.color}} niceborder" ng-if="s.type == 'countdown' && !s.params.hide">
46             <div class="card-header bg-{{s.params.color}} text-light" ng-if="s.params.title">
47               <h3 style="margin:0"><strong ng-bind="s.params.title"></strong></h3>
48             </div>
49             <div ng-controller="TimerController" ng-init="s.params.end?init(s.params.end):initStart()">
50               <div class="card-body text-center bg-dark text-light clock" style="font-size: 450%;" ng-if="duration > 0">{{ duration / 60 | time }} <span class="point">:</span> {{ duration % 60 | time }}</div>
51               <div class="card-body text-center bg-dark text-light" style="font-size: 450%;" ng-if="!duration || duration <= 0">{{ s.params.lead }}</div>
52             </div>
53           </div>
54
55           <div class="card niceborder bg-dark" ng-if="s.type == 'welcome' && !s.params.hide && s.params.kind == 'public'">
56             <div class="card-body text-light text-indent">
57               <h1 ng-if="!s.params.notitle">Bienvenue au challenge forensic&nbsp;!</h1>
58               <p class="lead text-justify">
59                 Durant ce challenge, les équipes doivent <strong>remonter des scénarii
60                 d'attaques</strong> auxquels nos systèmes d'information <strong>font face
61                 chaque jour</strong> : fuite de données, compromission d'un poste de
62                 travail, exploitation de vulnérabilités d'un site web, ...
63               </p>
64               <p class="lead text-justify">
65                 Pour valider un challenge, chaque participant va télécharger :
66                 soit des <strong>journaux d'évènements</strong>, des extraits de trafic réseau
67                 ou même des <strong>copies</strong> figées <strong>de la mémoire vive</strong> de machines
68                 malveillantes, pour <strong>essayer de comprendre</strong> comment l'attaquant a
69                 <strong>contourné la sécurité</strong> de la machine et quelles actions hostiles
70                 ont été effectuées.
71               </p>
72             </div>
73           </div>
74
75           <div class="card niceborder bg-dark" ng-if="s.type == 'message' && !s.params.hide">
76             <div class="card-body text-light">
77               <h1 ng-if="s.params.title"><strong ng-bind="s.params.title"></strong></h1>
78               <p ng-if="s.params.lead" class="lead text-justify" ng-bind="s.params.lead"></p>
79               <p ng-bind-html="s.params.html" ng-if="s.params.html"></p>
80               <p ng-if="s.params.text" ng-bind="s.params.text"></p>
81             </div>
82           </div>
83
84           <div class="card border-{{ s.params.color }} niceborder" ng-if="s.type == 'panel' && !s.params.hide">
85             <div class="card-header bg-{{ s.params.color }} text-white" ng-if="s.params.title">
86               <h3 style="margin:0" ng-bind="s.params.title"></h3>
87             </div>
88             <div class="card-body text-light" ng-if="s.params.text || s.params.lead">
89               <h4 ng-if="s.params.lead" class="card-title text-justify" ng-bind="s.params.lead"></h4>
90               <p class="card-text" ng-bind="s.params.text"></p>
91             </div>
92             <div class="card-body text-light" ng-if="s.params.html" ng-bind-html="s.params.html"></div>
93           </div>
94
95           <div class="card niceborder bg-dark" ng-if="s.type == 'exercice' && !s.params.hide">
96             <div class="card-body text-light">
97               <p class="lead">
98                 <strong>Challenge <em>{{ themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].title }}</em> du thème {{ themes[my.exercices[s.params.exercice].theme_id].name }}</strong>
99                 <small class="authors" ng-if="themes[my.exercices[s.params.exercice].theme_id].authors">par {{ themes[my.exercices[s.params.exercice].theme_id].authors | stripHTML }}</small>
100               </p>
101               <p ng-bind-html="my.exercices[s.params.exercice].statement"></p>
102               <ul class="list-inline text-secondary">
103                 <li>Rapporte <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
104                 <li ng-if="my.exercices[s.params.exercice].files"><ng-pluralize count="my.exercices[s.params.exercice].files.length" when="{'0': 'Aucun fichier disponible', 'one': '{} fichier disponible', 'other': '{} fichiers disponibles'}"></ng-pluralize></li>
105                 <li ng-if="my.exercices[s.params.exercice].hints.length"><ng-pluralize count="my.exercices[s.params.exercice].hints.length" when="{'0': 'Aucun indice disponible', 'one': '{} indice disponible', 'other': '{} indices disponibles'}"></ng-pluralize></li>
106                 <li>Tenté par <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].tried" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
107                 <li ng-if="my.exercices[s.params.exercice].tries"><ng-pluralize count="my.exercices[s.params.exercice].tries" when="{'0': 'Aucune tentative', 'one': '{} tentative', 'other': '{} tentatives'}"></ng-pluralize></li>
108                 <li>Résolu par <ng-pluralize count="themes[my.exercices[s.params.exercice].theme_id].exercices[s.params.exercice].solved" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
109               </ul>
110             </div>
111           </div>
112
113           <div ng-if="s.type == 'table' && !s.params.hide">
114             <table class="table table-striped table-sm table-dark">
115               <thead class="bg-light">
116                 <tr class="text-light">
117                   <th class="frotated"></th>
118                   <th class="rotated" ng-repeat="(tid,th) in themes" ng-if="s.params.themes.indexOf(tid-0) !== -1"><div class="bg-dark"><span>{{ th.name }}</span></div></th>
119                 </tr>
120               </thead>
121               <tbody class="table-bordered bg-secondary text-dark" ng-if="s.params.kind == 'levels'">
122                 <tr ng-repeat="lvl in s.params.levels">
123                   <th class="text-center"><nobr>Challenge {{ lvl }}</nobr></th>
124                   <td ng-repeat="(tid,th) in themes" class="text-center" ng-if="s.params.themes.indexOf(tid-0) !== -1">
125                     <span ng-repeat="exercice in th.exercices" ng-if="$index == lvl-1 && (exercice.tried || lvl == 1)" ng-class="{'text-primary': exercice.solved == 0, 'text-success': exercice.solved >= 1, 'text-bold': exercice.solved >= 1, 'text-warning': exercice.solved == 0 && exercice.tried}">
126                       <span ng-if="exercice.solved">{{ exercice.solved }}</span>
127                       <span ng-if="!exercice.solved">{{ exercice.tried }}</span>
128                     </span>
129                   </td>
130                 </tr>
131               </tbody>
132               <tbody class="table-bordered" ng-if="s.params.kind == 'teams'">
133                 <tr ng-repeat="team in rank | orderBy: 'rank' | limitTo: s.params.limit: s.params.begin" ng-if="s.params.teams.indexOf(team.id-0) !== -1" ng-controller="TeamController">
134                   <th class="text-center">{{ team.rank }}<sup ng-if="team.rank == 1">er</sup><sup ng-if="team.rank > 1">e</sup><br><span style="text-overflow: ellipsis; display: inline-block; width: 82px;overflow: hidden;">{{ team.name }}</span></th>
135                   <td ng-repeat="(tid,th) in themes" class="text-center" ng-if="mystats && s.params.themes.indexOf(tid-0) !== -1">
136                     <span ng-class="{'text-success': mystats.themes[tid].solved}">{{ mystats.themes[tid].solved }}/{{ mystats.themes[tid].total }}</span>
137                     <span ng-class="{'text-warning': mystats.themes[tid].solved < mystats.themes[tid].tried}">({{ mystats.themes[tid].tries }})</span>
138                   </td>
139                 </tr>
140               </tbody>
141               <tfoot ng-if="s.params.total" ng-init="team={id:0}">
142                 <tr ng-controller="TeamController">
143                   <td class="text-right text-dark">
144                     <span ng-if="s.params.kind == 'levels'">Résolus</span>
145                     <span ng-if="s.params.kind == 'teams'">Total résolus</span><br>
146                     Tentatives
147                   </td>
148                   <td class="table-bordered bg-dark" ng-repeat="(tid,th) in themes" class="text-center" ng-if="mystats && s.params.themes.indexOf(tid-0) !== -1">
149                     <strong>{{ mystats.themes[tid].solved }}</strong><br>
150                     {{ mystats.themes[tid].tries }}
151                   </td>
152                 </tr>
153               </tfoot>
154             </table>
155             </div>
156
157             <div class="card niceborder bg-dark" ng-if="s.type == 'rank' && !s.params.hide">
158             <table class="table table-bordered table-striped table-sm table-dark">
159               <thead class="thead-dark">
160                 <tr>
161                   <th class="text-right">Place</th>
162                   <th>Équipe</th>
163                   <th>Score</th>
164                 </tr>
165               </thead>
166               <tbody ng-if="s.params.which == 'general'">
167                 <tr ng-repeat="r in rank | orderBy: 'rank' | limitTo: s.params.limit: s.params.begin">
168                   <th class="text-right">{{ r.rank }}<sup ng-if="r.rank == 1">er</sup><sup ng-if="r.rank > 1">e</sup></th>
169                   <td>{{ r.name }}</td>
170                   <td>{{ r.score | number:0 }}</td>
171                 </tr>
172               </tbody>
173             </table>
174             </div>
175
176             <div class="card niceborder bg-dark" ng-if="s.type == 'carousel' && !s.params.hide">
177               <div class="card-header bg-{{ s.params.color }} text-white" ng-if="s.params.title">
178                 <h3 style="margin:0" ng-bind="s.params.title" class="text-dark" ng-if="s.params.color == 'light'"></h3>
179                 <h3 style="margin:0" ng-bind="s.params.title" ng-if="s.params.color != 'light'"></h3>
180               </div>
181               <div class="card-body bg-dark">
182
183                 <div class="carousel slide" data-interval="30000" style="height: 270px" autocarousel ng-if="s.params.kind == 'themes'">
184                   <ol class="carousel-indicators">
185                     <li data-slide-to="{{k}}" ng-repeat="(k, theme) in themes" ng-class="{active: $first}"></li>
186                   </ol>
187                   <div class="carousel-inner">
188                     <div class="carousel-item" ng-repeat="theme in themes" ng-class="{active: $first}">
189                       <div class="carousel-caption text-indent">
190                         <h3 class="text-left" ng-bind="theme.name"></h3>
191                         <p class="lead text-justify text-bold" style="font-size: 111%" ng-bind-html="theme.headline"></p>
192                         <p class="text-justify" ng-bind-html="theme.intro"></p>
193                       </div>
194                     </div>
195                   </div>
196                 </div>
197
198                 <div class="carousel slide" data-interval="7000" style="height: 100px" autocarousel ng-if="s.params.kind == 'teams'">
199                   <ol class="carousel-indicators">
200                     <li data-slide-to="{{k}}" ng-repeat="(k, team) in teams" ng-class="{active: $first}" ng-if="team.rank"></li>
201                   </ol>
202                   <div class="carousel-inner">
203                     <div class="carousel-item" ng-repeat="team in teams" ng-class="{active: $first}" ng-if="team.rank">
204                       <div class="carousel-caption">
205                         <p class="lead" style="margin: 10px" ng-bind="team.name"></p>
206                         <ul class="list-inline">
207                           <li>Classement&nbsp;: {{ team.rank }}<sup><ng-pluralize count="team.rank" when="{'one': 're', 'other': 'e'}"></ng-pluralize></sup></li>
208                           <li><ng-pluralize count="team.score" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
209                         </ul>
210                       </div>
211                     </div>
212                   </div>
213                 </div>
214
215                 <div class="carousel slide" data-interval="10000" style="height: 260px" autocarousel ng-if="s.params.kind == 'ranking'">
216                   <div class="carousel-inner">
217                     <div class="carousel-item" ng-repeat="(i,t) in pagesrank" ng-class="{active: $first}">
218                       <div class="carousel-caption">
219                         <table class="table table-bordered table-striped table-sm table-dark">
220                           <thead class="thead-dark">
221                             <tr>
222                               <th class="text-right">Place</th>
223                               <th>Équipe</th>
224                               <th>Score</th>
225                             </tr>
226                           </thead>
227                           <tbody>
228                             <tr ng-repeat="r in rank | orderBy: 'rank' | limitTo: 7: 7*i">
229                               <th class="text-right">{{ r.rank }}<sup ng-if="r.rank == 1">er</sup><sup ng-if="r.rank > 1">e</sup></th>
230                               <td>{{ r.name }}</td>
231                               <td>{{ r.score }}</td>
232                             </tr>
233                           </tbody>
234                         </table>
235                       </div>
236                     </div>
237                   </div>
238                 </div>
239
240                 <div class="carousel slide" data-interval="30000" style="height: 270px" autocarousel ng-if="s.params.kind == 'exercices'">
241                   <ol class="carousel-indicators">
242                     <li data-slide-to="{{k}}" ng-repeat="(k, exercice) in my.exercices" ng-class="{active: $first}"></li>
243                   </ol>
244                   <div class="carousel-inner">
245                     <div class="carousel-item" ng-repeat="(eid, exercice) in my.exercices" ng-class="{active: $first}">
246                       <div class="carousel-caption">
247                         <p class="lead">
248                           <strong>Challenge <em>{{ themes[exercice.theme_id].exercices[eid].title }}</em> du thème {{ themes[exercice.theme_id].name }}</strong>
249                           <br>
250                           <small class="authors" ng-if="themes[exercice.theme_id].authors">par {{ themes[exercice.theme_id].authors }}</small>
251                         </p>
252                         <p ng-bind-html="exercice.statement"></p>
253                         <ul class="list-inline text-secondary">
254                           <li>Rapporte <ng-pluralize count="themes[exercice.theme_id].exercices[eid].gain" when="{'0': 'aucun point', 'one': '{} point', 'other': '{} points'}"></ng-pluralize></li>
255                           <li ng-if="exercice.files"><ng-pluralize count="exercice.files.length" when="{'0': 'Aucun fichier disponible', 'one': '{} fichier disponible', 'other': '{} fichiers disponibles'}"></ng-pluralize></li>
256                           <li ng-if="exercice.hints.length"><ng-pluralize count="exercice.hints.length" when="{'0': 'Aucun indice disponible', 'one': '{} indice disponible', 'other': '{} indices disponibles'}"></ng-pluralize></li>
257                           <li>Tenté par <ng-pluralize count="themes[exercice.theme_id].exercices[eid].tried" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
258                           <li ng-if="exercice.tries"><ng-pluralize count="exercice.tries" when="{'0': 'Aucune tentative', 'one': '{} tentative', 'other': '{} tentatives'}"></ng-pluralize></li>
259                           <li>Résolu par <ng-pluralize count="themes[exercice.theme_id].exercices[eid].solved" when="{'0': 'aucune équipe', 'one': '{} équipe', 'other': '{} équipes'}"></ng-pluralize></li>
260                         </ul>
261                       </div>
262                     </div>
263                   </div>
264                 </div>
265
266               </div>
267             </div>
268
269         </div>
270       </div>
271
272       <div class="col-4">
273
274         <div ng-controller="EventsController" id="eventsList" style="position:fixed;padding-right:10px">
275           <div ng-repeat="e in events track by e.id" class="swap-animation" ng-cloak>
276             <div class="card card-sm niceborder" ng-class="e.kind">
277               <div class="card-header text-right">
278                 <small class="text-muted">{{ e.since | since }}</small>
279               </div>
280               <div class="card-body text-indent text-justify">
281                 <p class="card-text" ng-bind-html="e.txt"></p>
282               </div>
283             </div>
284           </div>
285
286           <div ng-controller="CountdownController" style="position: fixed; bottom: 109px; right: 0; width: 33vw; overflow: hidden; padding-top: 25px;" ng-cloak>
287             <div class="bg-light text-center text-dark" ng-if="time.hours === 0 || time.hours" style="margin-top: -5px; width: inherit; box-shadow: white 0px -10px 15px 0px;">
288               <div class="clock" ng-class="{expired: time.expired, end: time.end}" style="font-size: 50px">
289                 <span id="hours">{{ time.hours | time }}</span>
290                 <span class="point">:</span>
291                 <span id="min">{{ time.minutes | time }}</span>
292                 <span class="point">:</span>
293                 <span id="sec">{{ time.seconds | time }}</span>
294               </div>
295               <div style="font-size: 18px; margin-top: -15px; text-shadow: 0 0 6px #4eaee6;">
296                 <span ng-if="!time.end && !startIn">Temps restant du challenge forensic</span>
297                 <span ng-if="!time.end && startIn">Le challenge forensic va bientôt commencer&nbsp;!</span>
298                 <span ng-if="time.end">Le challenge forensic est terminé&nbsp;!</span>
299               </div>
300             </div>
301             <div class="clock" class="col-sm-6" ng-if="!(time.hours === 0 || time.hours)">
302               {{ time.start | date:"shortDate" }}
303             </div>
304           </div>
305
306           <div style="position: fixed; bottom: 0; right: 0; width: 33vw; height: 110px;" class="bg-dark">
307             <div class="carousel slide" id="carousel-logos" data-ride="carousel" data-interval="10500" style="height: inherit">
308               <div class="carousel-inner" style="height: inherit">
309                 <div class="carousel-item">
310                   <div class="carousel-caption" style="display: table; width: 100%">
311                     <a href="http://www.epita.fr/" class="align-middle text-center" style="display: table-cell; width: 55%"><img src="/img/epita.png" alt="Epita" style="height:100px"></a>
312                     <a href="http://www.epita.fr/" class="align-middle text-center" style="display: table-cell; width: 45%"><img src="/img/comcyber.png" alt="Réserves de cyberdéfense" style="height:100px"></a>
313                   </div>
314                 </div>
315                 <div class="carousel-item">
316                   <div class="carousel-caption" style="padding: 15px; display: table;">
317                     <h2 class="align-middle" style="display: table-cell;">Bienvenue au challenge forensic&nbsp;!</h2>
318                   </div>
319                 </div>
320                 <div class="carousel-item">
321                   <div class="carousel-caption text-indent" style="padding: 0 15px; display: table; line-height: 1.3">
322                     <p class="text-justify text-bold align-middle" style="display: table-cell; font-size: 111%">
323                       Ce challenge met en scène des scénarii d'attaques auxquels
324                       nos systèmes d'information font face chaque jour.
325                     </p>
326                   </div>
327                 </div>
328                 <div class="carousel-item">
329                   <div class="carousel-caption text-indent" style="padding: 0 10px; display: table; line-height: 1.3">
330                     <p class="text-justify text-bold align-middle" style="display: table-cell; font-size: 111%">
331                       Les {{ teams | objectLength }} équipes doivent, en 4 heures, retracer les attaques à la
332                       recherche des données confidentielles exfiltrées.
333                     </p>
334                   </div>
335                 </div>
336                 <div class="carousel-item active">
337                   <div class="carousel-caption" style="padding: 5px; display: table;">
338                     <div class="align-middle" style="display: table-cell; width: 30%">
339                       <a href="https://srs.epita.fr/"><img src="/img/srs.png" alt="Laboratoire SRS" style="max-width:100%; max-height:90px"></a>
340                     </div>
341                     <p class="text-bold align-middle" style="font-size: 110%; display: table-cell; width: 70%;">
342                       Les challenges ont été réalisés par les étudiants de la
343                       spécialisation SRS de l'Épita.
344                     </p>
345                   </div>
346                 </div>
347                 <div class="carousel-item">
348                   <div class="carousel-caption" style="padding: 5px; display: table">
349                     <p class="text-bold align-middle" style="font-size: 111%; display: table-cell; width: 70%">
350                       Avec le parrainage des réserves de cyberdéfense
351                     </p>
352                     <div class="align-middle" style="display: table-cell; width: 30%">
353                       <img src="/img/comcyber.png" alt="Réserves de cyberdéfense" style="max-width:100%; max-height: 80px">
354                     </div>
355                   </div>
356                 </div>
357                 <div class="carousel-item">
358                   <div class="carousel-caption">
359                     <table class="table table-sm table-striped table-dark">
360                       <tbody>
361                         <tr>
362                           <td>{{ settings.start - 1800000 | date:"HH'&nbsp;h&nbsp;'mm" }}</td>
363                           <td>Accueil des équipes</td>
364                         </tr>
365                         <tr>
366                           <td>{{ settings.start | date:"HH'&nbsp;h&nbsp;'mm" }}</td>
367                           <td>Début du challenge</td>
368                         </tr>
369                         <tr>
370                           <td>{{ settings.end | date:"HH'&nbsp;h&nbsp;'mm" }}</td>
371                           <td>Fin du challenge</td>
372                         </tr>
373                         <tr>
374                           <td>{{ settings.awards | date:"HH'&nbsp;h&nbsp;'mm" }}</td>
375                           <td>Remise des prix</td>
376                         </tr>
377                       </tbody>
378                     </table>
379                   </div>
380                 </div>
381                 <div class="carousel-item">
382                   <div class="carousel-caption" style="display: table;">
383                     <p class="text-bold align-middle" style="display: table-cell; padding: 5px">
384                       Retrouvez les solutions des challenges dès demain sur :
385                       <span style="display: block; font-size: 135%" class="text-center">
386                         <a style="font-family: mono" href="https://fic.srs.epita.fr/"><span class="text-info">https://fic.srs.epita.fr/</span></a>
387                       </span>
388                     </p>
389                   </div>
390                 </div>
391
392               </div>
393             </div>
394           </div>
395         </div>
396       </div>
397
398     </div>
399
400     <script src="/js/jquery.min.js"></script>
401     <script src="/js/bootstrap.min.js"></script>
402     <script src="/js/angular-animate.min.js"></script>
403     <script src="/js/angular-route.min.js"></script>
404     <script src="/js/angular-sanitize.min.js"></script>
405     <script src="/js/i18n/angular-locale_fr-fr.js"></script>
406     <script src="/js/dashboard.js"></script>
407     <script src="/js/common.js"></script>
408   </body>
409 </html>