From 45069d4fbb825b6e6e2d7cc273ecd254e2f276d8 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 8 Mar 2020 12:48:25 +0100 Subject: [PATCH] admin: replace notifications with bootstrap toast --- admin/index.go | 16 +-- admin/static/index.html | 16 +-- admin/static/js/app.js | 261 +++++++++++++++++++--------------------- 3 files changed, 133 insertions(+), 160 deletions(-) diff --git a/admin/index.go b/admin/index.go index 444157a9..3854e250 100644 --- a/admin/index.go +++ b/admin/index.go @@ -111,20 +111,12 @@ const indextpl = `
-
-
- - -
    -
  • -
- - -
-
-
+
+ +
+ diff --git a/admin/static/index.html b/admin/static/index.html index 0096007b..43a89dcd 100644 --- a/admin/static/index.html +++ b/admin/static/index.html @@ -109,20 +109,12 @@
-
-
- - -
    -
  • -
- - -
-
-
+
+ +
+ diff --git a/admin/static/js/app.js b/admin/static/js/app.js index 8c89dcd9..b4a4c4d7 100644 --- a/admin/static/js/app.js +++ b/admin/static/js/app.js @@ -127,7 +127,54 @@ angular.module("FICApp") }); } } - }]); + }]) + + .component('toast', { + bindings: { + date: '=', + msg: '=', + timeout: '=', + title: '=', + variant: '=', + yesNo: '=', + onyes: '=', + onno: '=', + }, + controller: function($element) { + if (this.timeout === 0) + $element.children(0).toast({autohide: false}); + else if (!this.timeout && this.timeout !== 0) + $element.children(0).toast({delay: 7000}); + else + $element.children(0).toast({delay: this.timeout}); + $element.children(0).toast('show'); + this.yesFunc = function() { + $element.children(0).toast('dispose'); + if (this.onyes) + this.onyes(); + } + this.noFunc = function() { + $element.children(0).toast('dispose'); + if (this.onno) + this.onno(); + } + }, + template: `` + }); angular.module("FICApp") .factory("Version", function($resource) { @@ -395,15 +442,27 @@ angular.module("FICApp") refresh(); $interval(refresh, 10000); + $rootScope.toasts = []; + $rootScope.addToast = function(kind, title, msg, yesFunc, noFunc, tmout) { + $rootScope.toasts.unshift({ + variant: kind, + title: title, + msg: msg, + timeout: tmout, + yesFunc: yesFunc, + noFunc: noFunc, + }); + } + $rootScope.staticFilesNeedUpdate = 0; $rootScope.regenerateStaticFiles = function() { Settings.get().$promise.then(function(config) { config.generation = (new Date()).toISOString(); config.$update(function() { $rootScope.staticFilesNeedUpdate = 0; - $rootScope.newBox('success', "Regeneration in progress..."); + $scope.addToast('success', "Regeneration in progress..."); }, function (response) { - $rootScope.newBox('success', 'An error occurs when saving settings:', response.data.errmsg); + $scope.addToast('success', 'An error occurs when saving settings:', response.data.errmsg); }) }) } @@ -435,76 +494,6 @@ angular.module("FICApp") $scope.monitor = Monitor.get(); }) - .controller("DIWEBoxController", function($scope, $rootScope, $interval, $timeout) { - function updBox() { - while ($rootScope._newBoxes.length > 0) { - var b = $rootScope._newBoxes.shift(); - $scope.boxes.unshift(b); - var id = $scope.boxes.length - 1; - b.cancel = function() { - $scope.boxes.pop($scope.boxes.indexOf(b)); - } - if (b.timeout >= 0) - $timeout(function() { b.cancel(); }, b.timeout); - } - } - - $rootScope._newBoxes = new Array(); - $rootScope.newBox = function(kind, title, msg, tmout) { - if (kind === undefined) { kind = 'default'; } - if (msg === undefined) { msg = ''; } - if (tmout === undefined) { tmout = 5000; } - - var mybox = { - 'kind': kind, - 'title': title, - 'timeout': tmout, - }; - - if (Array.isArray(msg)) - mybox['list'] = msg; - else - mybox['msg'] = msg; - - $rootScope._newBoxes.push(mybox); - }; - $rootScope.newYesNoBox = function(kind, title, msg, yesFunc, noFunc, tmout) { - if (kind === undefined) { kind = 'default'; } - if (msg === undefined) { msg = ''; } - if (tmout === undefined) { tmout = 5000; } - - var mybox; - - var yesFn = function() { - mybox.cancel(); - if (yesFunc !== undefined) { - yesFunc(); - } - } - var noFn = function() { - mybox.cancel(); - if (noFunc !== undefined) { - noFunc(); - } - } - - mybox = { - 'kind': kind, - 'title': title, - 'msg': msg, - 'yes': yesFn, - 'no': noFn, - 'timeout': tmout, - }; - - $rootScope._newBoxes.push(mybox); - }; - $scope.boxes = new Array(); - - updBox(); - $interval(updBox, 750); - }) - .controller("SettingsController", function($scope, $rootScope, Settings, ROSettings, $location, $http, $interval) { $scope.displayDangerousActions = false; $scope.config = Settings.get(); @@ -555,14 +544,14 @@ angular.module("FICApp") var state = this.config.enableExerciceDepend; this.config.unlockedChallengeDepth = (this.config.enableExerciceDepend?this.config.unlockedChallengeDepth:-1) this.config.$update(function(response) { - $rootScope.newBox('success', msg); + $scope.addToast('success', msg); response.enableExerciceDepend = response.unlockedChallengeDepth >= 0; $rootScope.settings.start = new Date(nStart); $rootScope.settings.end = new Date(nEnd); $rootScope.settings.generation = new Date(nGen); $rootScope.settings.activateTime = new Date(aTime); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when saving settings:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when saving settings:', response.data.errmsg); }); } $scope.regenerate = function() { @@ -577,7 +566,7 @@ angular.module("FICApp") var f = new Date(ts + 120000 + this.duration * 60000); this.config.end = f.toISOString(); - $rootScope.newYesNoBox('info', 'Challenge ready to start,', 'propagate the changes?', + $scope.addToast('info', 'Challenge ready to start,', 'propagate the changes?', function() { $scope.saveSettings(); }); @@ -593,14 +582,14 @@ angular.module("FICApp") "teams": "En validant, vous supprimerez l'ensemble des équipes enregistreées.", "game": "En validant, vous supprimerez toutes les tentatives, les validations, ... faites par les équipes.", } - $rootScope.newYesNoBox('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?', + $scope.addToast('warning', txts[type], 'Êtes-vous sûr de vouloir continuer ?', function() { if (confirm("Êtes-vous vraiment sûr ?\n" + txts[type])) { $http.post("/api/reset", {"type": type}).then(function(time) { - $rootScope.newBox('success', type + 'reseted'); + $scope.addToast('success', type + 'reseted'); $location.url("/"); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when reseting ' + type + ':', response.data.errmsg); }); } @@ -615,28 +604,28 @@ angular.module("FICApp") question = 'Faire une synchronisation intégrale ?' url = "/api/sync/deep" } - $rootScope.newYesNoBox('warning', question, '', + $scope.addToast('warning', question, '', function() { $scope.deepSyncInProgress = true; $http.post(url).then(function() { $scope.deepSyncInProgress = false; - $rootScope.newBox('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); + $scope.addToast('success', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); }, function(response) { $scope.deepSyncInProgress = false; - $rootScope.newBox('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); + $scope.addToast('warning', 'Synchronisation intégrale terminée.', 'Voir le rapport.', 15000); }); }); }; $scope.speedyDeepSync = function() { - $rootScope.newYesNoBox('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '', + $scope.addToast('warning', 'Faire une synchronisation profonde rapide, sans s\'occuper des fichiers ?', '', function() { $scope.deepSyncInProgress = true; $http.post("/api/sync/speed").then(function() { $scope.deepSyncInProgress = false; - $rootScope.newBox('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', 15000); + $scope.addToast('success', 'Synchronisation profonde rapide terminée.', 'Voir le rapport.', 15000); }, function(response) { $scope.deepSyncInProgress = false; - $rootScope.newBox('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', 15000); + $scope.addToast('warning', 'Synchronisation profinde rapide terminée.', 'Voir le rapport.', 15000); }); }); }; @@ -667,7 +656,7 @@ angular.module("FICApp") }); }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); } ); } @@ -703,7 +692,7 @@ angular.module("FICApp") $scope.certificates = Certificate.query(); $scope.selectedTeam = null; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to associate certificate:', response.data.errmsg); } ); }; @@ -712,7 +701,7 @@ angular.module("FICApp") $http.post("/api/ca/new", $scope.newca).then(function() { $scope.ca = CACertificate.get(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating CA:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating CA:', response.data.errmsg); }); }; $scope.renewCA = function() { @@ -723,21 +712,21 @@ angular.module("FICApp") $http.post("/api/certs").then(function() { $scope.certificates = Certificate.query(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating certificate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating certificate:', response.data.errmsg); }); }; $scope.generateHtpasswd = function() { $http.post("/api/htpasswd").then(function() { - $rootScope.newBox('success', 'Fichier htpasswd généré avec succès'); + $scope.addToast('success', 'Fichier htpasswd généré avec succès'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when generating htpasswd file:', response.data.errmsg); }); }; $scope.removeHtpasswd = function() { $http.delete("/api/htpasswd").then(function() { - $rootScope.newBox('success', 'Fichier htpasswd supprimé avec succès'); + $scope.addToast('success', 'Fichier htpasswd supprimé avec succès'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when deleting htpasswd file:', response.data.errmsg); }); }; }) @@ -1006,9 +995,9 @@ angular.module("FICApp") $scope.someUpdt = false; var prms = Scene.update({ screenId: $scope.screenid }, $scope.display); prms.$promise.then(function() { - $rootScope.newBox('success', 'Scene successfully published!'); + $scope.addToast('success', 'Scene successfully published!'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when saving scene:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when saving scene:', response.data.errmsg); }); }; $scope.addSide = function() { @@ -1361,7 +1350,7 @@ angular.module("FICApp") $scope.comm.ndescription = "Création de la tâche"; $scope.saveDescription(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to save claim:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to save claim:', response.data.errmsg); }); } } @@ -1404,12 +1393,12 @@ angular.module("FICApp") $scope.themes = Theme.query(); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); + $scope.addToast('success', 'Synchronisation de la liste des thèmes terminée avec succès.'); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing theme list:', response.data.errmsg); }); }; }) @@ -1432,7 +1421,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/"); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete theme:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete theme:', response.data.errmsg); }); } }) @@ -1479,7 +1468,7 @@ angular.module("FICApp") }) $scope.exercice = {}; $rootScope.staticFilesNeedUpdate++; - $rootScope.newBox('success', 'Édition de masse terminée avec succès'); + $scope.addToast('success', 'Édition de masse terminée avec succès'); } $scope.show = function(id) { @@ -1493,7 +1482,7 @@ angular.module("FICApp") var work = []; var go = function() { if (!work.length) { - $rootScope.newBox('info', "Synchronisation des exercices terminée."); + $scope.addToast('info', "Synchronisation des exercices terminée."); $scope.inSync = false; return; } @@ -1547,12 +1536,12 @@ angular.module("FICApp") $scope.exercices = ThemedExercice.query({ themeId: $scope.theme.id }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('warning', null, response.data, -1); + $scope.addToast('warning', null, response.data, -1); else - $rootScope.newBox('success', 'Synchronisation de la liste des exercices terminée avec succès.'); + $scope.addToast('success', 'Synchronisation de la liste des exercices terminée avec succès.'); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchrinizing exercices:', response.data.errmsg); }); }; }) @@ -1591,12 +1580,12 @@ angular.module("FICApp") $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de l'exercice terminée avec succès."); + $scope.addToast('success', "Synchronisation de l'exercice terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing exercice:', response.data.errmsg); }); }; @@ -1606,7 +1595,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/" + tid); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete exercice:', response.data.errmsg); }); } $scope.saveExercice = function() { @@ -1618,7 +1607,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $location.url("/themes/" + $scope.exercice.idTheme + "/exercices/" + $scope.exercice.id); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to save exercice:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to save exercice:', response.data.errmsg); }); } } @@ -1684,7 +1673,7 @@ angular.module("FICApp") $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); $('#updHistory').modal('hide'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when updating history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); }); } } @@ -1697,7 +1686,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.history = ExerciceHistory.query({ exerciceId: $routeParams.exerciceId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); }); } }) @@ -1719,7 +1708,7 @@ angular.module("FICApp") }).then(function(response) { $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing file dep:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing file dep:', response.data.errmsg); }); $rootScope.staticFilesNeedUpdate++; return false; @@ -1739,12 +1728,12 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId }); if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste de fichiers terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste de fichiers terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); }); }; }) @@ -1760,7 +1749,7 @@ angular.module("FICApp") $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete hint:', response.data.errmsg); }); } $scope.saveHint = function() { @@ -1782,12 +1771,12 @@ angular.module("FICApp") $scope.hints = ExerciceHint.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste d'indices terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste d'indices terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing hints list:', response.data.errmsg); }); }; }) @@ -1819,7 +1808,7 @@ angular.module("FICApp") $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); }); } $scope.saveFlag = function() { @@ -1845,10 +1834,10 @@ angular.module("FICApp") method: "POST" }).then(function(response) { flag.test_str = ""; - $rootScope.newBox('success', "Flag Ok !"); + $scope.addToast('success', "Flag Ok !"); }, function(response) { flag.test_str = ""; - $rootScope.newBox('danger', 'An error occurs: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs: ', response.data.errmsg); }); } } @@ -1863,12 +1852,12 @@ angular.module("FICApp") $scope.flags = ExerciceFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; if (response.data) - $rootScope.newBox('danger', response.data); + $scope.addToast('danger', response.data); else - $rootScope.newBox('success', "Synchronisation de la liste de drapeaux terminée avec succès."); + $scope.addToast('success', "Synchronisation de la liste de drapeaux terminée avec succès."); }, function(response) { $scope.inSync = false; - $rootScope.newBox('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when synchronizing flags list:', response.data.errmsg); }); }; }) @@ -1925,7 +1914,7 @@ angular.module("FICApp") $scope.quiz = ExerciceMCQFlag.query({ exerciceId: $routeParams.exerciceId }); $rootScope.staticFilesNeedUpdate++; }, function(response) { - $rootScope.newBox('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when trying to delete flag:', response.data.errmsg); }); } $scope.saveQuiz = function() { @@ -1975,7 +1964,7 @@ angular.module("FICApp") $http.post("/api/disableinactiveteams").then(function() { $scope.teams = Team.query(); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when disabling inactive teams:', response.data.errmsg); }); } $scope.show = function(id) { @@ -2022,8 +2011,8 @@ angular.module("FICApp") } $scope.deleteTeam = function() { backName = this.team.name; - this.team.$remove(function() { $rootScope.newBox('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); $rootScope.staticFilesNeedUpdate++; }, - function(response) { $rootScope.newBox('danger', 'An error occurs during suppression of the team:', response.data.errmsg); }); + this.team.$remove(function() { $scope.addToast('success', 'Team ' + backName + ' successfully removed.'); $location.url("/teams/"); $rootScope.staticFilesNeedUpdate++; }, + function(response) { $scope.addToast('danger', 'An error occurs during suppression of the team:', response.data.errmsg); }); } $scope.showStats = function() { $location.url("/teams/" + $scope.team.id + "/stats"); @@ -2053,9 +2042,9 @@ angular.module("FICApp") certificate.serial = parseInt(certificate.id).toString(16); }); }); - $rootScope.newBox('success', 'Certificate successfully dissociated!'); + $scope.addToast('success', 'Certificate successfully dissociated!'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when dissociating certiticate:', response.data.errmsg); }); } }) @@ -2071,7 +2060,7 @@ angular.module("FICApp") $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); }, function(response) { if (response.data) - $rootScope.newBox('danger', 'An error occurs when creating user association: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when creating user association: ', response.data.errmsg); }); } } @@ -2081,7 +2070,7 @@ angular.module("FICApp") function() { $scope.associations = TeamAssociation.query({ teamId: $routeParams.teamId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing user association: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing user association: ', response.data.errmsg); }); } }) @@ -2105,7 +2094,7 @@ angular.module("FICApp") $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); $('#updHistory').modal('hide'); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when updating history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when updating history item: ', response.data.errmsg); }); } } @@ -2118,7 +2107,7 @@ angular.module("FICApp") $rootScope.staticFilesNeedUpdate++; $scope.history = TeamHistory.query({ teamId: $routeParams.teamId }); }, function(response) { - $rootScope.newBox('danger', 'An error occurs when removing history item: ', response.data.errmsg); + $scope.addToast('danger', 'An error occurs when removing history item: ', response.data.errmsg); }); } })