admin/ui: Split settings page into sync and settings pages

This commit is contained in:
nemunaire 2022-05-19 21:15:25 +02:00
parent 70bad90756
commit 9fe66c563b
6 changed files with 211 additions and 164 deletions

View File

@ -80,7 +80,6 @@ const indextpl = `<!DOCTYPE html>
<li class="nav-item"><a class="nav-link" href="pki">PKI</a></li>
<li class="nav-item"><a class="nav-link" href="themes">Thèmes</a></li>
<li class="nav-item"><a class="nav-link" href="exercices">Exercices</a></li>
<li class="nav-item"><a class="nav-link" href="files">Fichiers</a></li>
<li class="nav-item"><a class="nav-link" href="public/0">Public</a></li>
<li class="nav-item"><a class="nav-link" href="events">&Eacute;vénements</a></li>
<li class="nav-item"><a class="nav-link" href="claims" ng-controller="ClaimsTinyListController">
@ -88,6 +87,11 @@ const indextpl = `<!DOCTYPE html>
<span class="badge badge-{{ "{{ priorities[myClaimsMaxLevel] }}" }}" ng-show="myClaims">{{ "{{ myClaims }}" }}</span>
<span class="badge badge-{{ "{{ priorities[newClaimsMaxLevel] }}" }}" ng-show="newClaims">{{ "{{ newClaims }}" }}</span>
</a></li>
<li class="nav-item">
<a class="nav-link" href="sync" ng-show="settings.wip">
Synchronisation
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="settings">
Paramètres

View File

@ -68,7 +68,10 @@ func declareStaticRoutes(router *gin.RouterGroup, cfg *settings.Settings, baseUR
router.GET("/pki/*_", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/settings/*_", func(c *gin.Context) {
router.GET("/settings", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/sync", func(c *gin.Context) {
serveIndex(c)
})
router.GET("/teams/*_", func(c *gin.Context) {

View File

@ -13,6 +13,10 @@ angular.module("FICApp", ["ngRoute", "ngResource", "ngSanitize"])
controller: "ExerciceController",
templateUrl: "views/exercice.html"
})
.when("/sync", {
controller: "SyncController",
templateUrl: "views/sync.html"
})
.when("/settings", {
controller: "SettingsController",
templateUrl: "views/settings.html"
@ -499,7 +503,7 @@ angular.module("FICApp")
$scope.monitor = Monitor.get();
})
.controller("SettingsController", function($scope, $rootScope, Settings, SettingsChallenge, ROSettings, $location, $http, $interval) {
.controller("SettingsController", function($scope, $rootScope, Settings, SettingsChallenge, $location, $http, $interval) {
$scope.displayDangerousActions = false;
$scope.config = Settings.get();
$scope.config.$promise.then(function(response) {
@ -509,7 +513,6 @@ angular.module("FICApp")
$rootScope.settings.generation = new Date(response.generation);
$rootScope.settings.activateTime = new Date(response.activateTime);
})
$scope.configro = ROSettings.get();
$scope.challenge = SettingsChallenge.get();
$scope.duration = 360;
@ -520,34 +523,6 @@ angular.module("FICApp")
$scope.config.unlockedChallengeDepth = -1;
};
var needRefreshSyncReportWhenReady = false;
var refreshSyncReport = function() {
needRefreshSyncReportWhenReady = false;
$http.get("full_import_report.json").then(function(response) {
$scope.syncReport = response.data;
})
};
refreshSyncReport()
var progressInterval = $interval(function() {
$http.get("api/sync/deep").then(function(response) {
if (response.data.progress && response.data.progress != 255)
needRefreshSyncReportWhenReady = true;
else if (needRefreshSyncReportWhenReady)
refreshSyncReport();
if (response.data && response.data.progress)
$scope.syncProgress = Math.floor(response.data.progress * 100 / 255) + " %";
else
$scope.syncProgress = response.data;
}, function(response) {
if (response.data && response.data.errmsg)
$scope.syncProgress = response.data.errmsg;
else
$scope.syncProgress = response.data;
})
}, 1500);
$scope.$on('$destroy', function () { $interval.cancel(progressInterval); });
$scope.saveChallengeInfo = function() {
this.challenge.$update(function(response) {
$scope.addToast('success', 'Infos du challenge mises à jour avec succès !');
@ -621,6 +596,40 @@ angular.module("FICApp")
}
});
};
})
.controller("SyncController", function($scope, $rootScope, ROSettings, $location, $http, $interval) {
$scope.displayDangerousActions = false;
$scope.configro = ROSettings.get();
var needRefreshSyncReportWhenReady = false;
var refreshSyncReport = function() {
needRefreshSyncReportWhenReady = false;
$http.get("full_import_report.json").then(function(response) {
$scope.syncReport = response.data;
})
};
refreshSyncReport()
var progressInterval = $interval(function() {
$http.get("api/sync/deep").then(function(response) {
if (response.data.progress && response.data.progress != 255)
needRefreshSyncReportWhenReady = true;
else if (needRefreshSyncReportWhenReady)
refreshSyncReport();
if (response.data && response.data.progress)
$scope.syncProgress = Math.floor(response.data.progress * 100 / 255) + " %";
else
$scope.syncProgress = response.data;
}, function(response) {
if (response.data && response.data.errmsg)
$scope.syncProgress = response.data.errmsg;
else
$scope.syncProgress = response.data;
})
}, 1500);
$scope.$on('$destroy', function () { $interval.cancel(progressInterval); });
$scope.deepSyncInProgress = false;
$scope.deepSync = function(theme) {
if (theme) {

View File

@ -1,7 +1,13 @@
<h2>Exercices
<button type="button" ng-click="syncFull()" ng-class="{'disabled': inSync}" class="float-right btn btn-sm btn-secondary"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Synchroniser</button>
<small style="height: 0px;">
<div class="btn-group btn-group-toggle float-right mr-2">
<div class="d-flex justify-content-between">
<h2>Exercices</h2>
<div class="align-self-center">
<a
href="files"
class="btn btn-sm btn-info"
>
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> Voir les fichiers
</a>
<div class="btn-group btn-group-toggle ml-2 mr-1">
<label class="btn btn-sm btn-secondary" ng-class="{active: syncFiles, 'btn-warning': syncFiles}">
<input type="checkbox" ng-model="syncFiles"> Fichiers
</label>
@ -12,8 +18,16 @@
<input type="checkbox" ng-model="syncFlags"> Flags
</label>
</div>
</small>
</h2>
<button
type="button"
ng-click="syncFull()"
ng-class="{'disabled': inSync}"
class="btn btn-sm btn-secondary"
>
<span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Synchroniser
</button>
</div>
</div>
<div class="progress" ng-if="inSync">
<div class="progress-bar" style="width: {{ done * 100 / total }}%;"></div>

View File

@ -1,13 +1,13 @@
<div class="row">
<div class="col-8">
<div class="row mb-4">
<div class="col-9">
<form ng-submit="saveSettings()" class="card my-3">
<div class="card-header">
<button ng-click="regenerate()" class="float-right btn btn-info" type="button"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Regénérer les fichiers statiques</button>
<button type="submit" class="float-right btn btn-success"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Propager ces paramètres</button>
<h2 class="mb-0">
Paramètres
</h2>
</div>
<div class="card-body">
<div class="card-body pb-1">
<input type="hidden" class="form-control form-control-sm" id="lastRegeneration" ng-model="config.generation">
<div class="form-group row">
@ -99,82 +99,9 @@
Il restera : {{ timeRemaining - activateTimeCountDown | timer }}
</small>
</div>
<div class="col text-right">
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-save" aria-hidden="true"></span> Propager ces paramètres</button>
</div>
</div>
</div>
</form>
<form ng-submit="saveChallengeInfo()" class="card my-3">
<div class="card-header">
<button type="submit" class="btn btn-success float-right" title="Enregistrer les modifications aux infos du challenge"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
<button ng-show="config.wip || !timeProgression || displayDangerousActions" ng-click="reset('challengeInfo');" type="button" class="mx-3 btn btn-danger float-right" title="Remettre les infos du challenge à zéro"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span></button>
<h3 class="mb-0">Infos challenge</h3>
</div>
<div class="card-body">
<div class="form-group row">
<label for="challengeTitle" class="col-sm-3 col-form-label col-form-label-sm">Nom du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeTitle" ng-model="challenge.title">
</div>
</div>
<div class="form-group row">
<label for="challengeSubtitle" class="col-sm-3 col-form-label col-form-label-sm">Sous-titre du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeSubtitle" ng-model="challenge.subtitle">
</div>
</div>
<div class="form-group row">
<label for="challengeAuthors" class="col-sm-3 col-form-label col-form-label-sm">Auteur du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeAuthors" ng-model="challenge.authors">
</div>
</div>
<div class="form-group row">
<label for="challengeVideoslink" class="col-sm-3 col-form-label col-form-label-sm">Liens solutions</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeVideoslink" ng-model="challenge.videoslink">
</div>
</div>
<div class="form-group row">
<label for="challengeDescription" class="col-sm-3 col-form-label col-form-label-sm">Description</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeDescription" ng-model="challenge.description">
</textarea>
</div>
</div>
<div class="form-group row">
<label for="challengeRules" class="col-sm-3 col-form-label col-form-label-sm">Règles</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeRules" ng-model="challenge.rules">
</textarea>
</div>
</div>
<div class="form-group row">
<label for="challengeYourMission" class="col-sm-3 col-form-label col-form-label-sm">Mission</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeYourMission" ng-model="challenge.your_mission">
</textarea>
</div>
</div>
</div>
</form>
</div>
<div class="col-4">
<form ng-submit="saveSettings()" class="card my-3">
<div class="card-header">
<h2 class="mb-0">Options</h2>
</div>
<div class="card-body px-0 py-2">
<div class="card-body pl-0 pt-3 border-top" style="columns: 2;">
<div class="form-check">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="config.allowRegistration">
@ -283,61 +210,104 @@
</div>
</form>
</div>
</div>
<div class="card mb-5" ng-show="!config.wip && timeProgression && !displayDangerousActions">
<div class="card-header bg-secondary text-light">
Synchronisation et suppressions de masse
<form ng-submit="saveChallengeInfo()" class="card my-3">
<div class="card-header">
<button type="submit" class="btn btn-success float-right" title="Enregistrer les modifications aux infos du challenge"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
<button ng-show="config.wip || !timeProgression || displayDangerousActions" ng-click="reset('challengeInfo');" type="button" class="mx-3 btn btn-danger float-right" title="Remettre les infos du challenge à zéro"><span class="glyphicon glyphicon-remove-sign" aria-hidden="true"></span></button>
<h3 class="mb-0">Infos challenge</h3>
</div>
<div class="card-body">
<div class="form-group row">
<label for="challengeTitle" class="col-sm-3 col-form-label col-form-label-sm">Nom du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeTitle" ng-model="challenge.title">
</div>
</div>
<div class="form-group row">
<label for="challengeSubtitle" class="col-sm-3 col-form-label col-form-label-sm">Sous-titre du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeSubtitle" ng-model="challenge.subtitle">
</div>
</div>
<div class="form-group row">
<label for="challengeAuthors" class="col-sm-3 col-form-label col-form-label-sm">Auteur du challenge</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeAuthors" ng-model="challenge.authors">
</div>
</div>
<div class="form-group row">
<label for="challengeVideoslink" class="col-sm-3 col-form-label col-form-label-sm">Liens solutions</label>
<div class="col-sm-9">
<input type="text" class="form-control form-control-sm" id="challengeVideoslink" ng-model="challenge.videoslink">
</div>
</div>
<div class="form-group row">
<label for="challengeDescription" class="col-sm-3 col-form-label col-form-label-sm">Description</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeDescription" ng-model="challenge.description">
</textarea>
</div>
</div>
<div class="form-group row">
<label for="challengeRules" class="col-sm-3 col-form-label col-form-label-sm">Règles</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeRules" ng-model="challenge.rules">
</textarea>
</div>
</div>
<div class="form-group row">
<label for="challengeYourMission" class="col-sm-3 col-form-label col-form-label-sm">Mission</label>
<div class="col-sm-9">
<textarea class="form-control form-control-sm" id="challengeYourMission" ng-model="challenge.your_mission">
</textarea>
</div>
</div>
</div>
</form>
</div>
<div class="card-body">
<div class="form-check">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="displayDangerousActions">
<strong class="custom-control-label">Je sais ce que le challenge a démarré ET <span style="text-decoration: underline red;">j'ai réalisé une sauvegarde de la base de données il y a moins d'une minute</span> ET je sais que c'est <span style="text-decoration: underline yellow;">une très mauvaise idée de cocher cette case</span>, mais j'y suis obligé pour de bonnes raisons.</strong>
</label>
<div class="col-3 pt-2 d-flex flex-column justify-content-between">
<div>
<div class="d-flex flex-column">
<button ng-click="regenerate()" class="btn btn-info my-1" type="button"><span class="glyphicon glyphicon-share" aria-hidden="true"></span> Regénérer les fichiers statiques</button>
</div>
<hr>
<h4>Changements anticipés</h4>
</div>
</div>
</div>
<div class="row mb-5" ng-show="config.wip || !timeProgression || displayDangerousActions">
<div class="col-8 card text-light bg-dark">
<div class="card-body">
<dl class="row">
<dt class="col-3">Synchronisation</dt>
<dd class="col-9" title="{{ configro['sync-type'] }}" ng-bind="configro.sync"></dd>
<dt class="col-3" ng-if="configro['sync-id']">ID</dt>
<dd class="col-9" ng-if="configro['sync-id']">{{ configro['sync-id'] }}</dd>
</dl>
<div class="card" ng-show="!config.wip && timeProgression && !displayDangerousActions">
<div class="card-header bg-secondary text-light">
<h5 class="mb-0">
Synchronisation et suppressions de masse
</h5>
</div>
<div class="card-body pl-0 pr-3 pt-3">
<div class="form-check pl-2">
<label class="custom-control custom-checkbox text-justify">
<input class="custom-control-input" type="checkbox" ng-model="displayDangerousActions">
<strong class="custom-control-label">Je sais ce que le challenge a démarré ET <span style="text-decoration: underline red;">j'ai réalisé une sauvegarde de la base de données il y a moins d'une minute</span> ET je sais que c'est <span style="text-decoration: underline yellow;">une très mauvaise idée de cocher cette case</span>, mais j'y suis obligé pour de bonnes raisons.</strong>
</label>
</div>
</div>
</div>
<div class="float-right" ng-if="configro.sync">
{{ syncProgress }}
</div>
<div ng-if="syncReport">
Dernier import&nbsp;: {{ syncReport._date[1] }}
</div>
<div class="text-left" ng-if="configro.sync">
<button ng-if="configro['sync-type'] === 'GitImporter'" type="button" class="btn btn-info" ng-click="baseSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> Pull</button>
<div class="btn-group">
<button type="button" class="btn btn-secondary" ng-click="deepSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Synchronisation intégrale</button>
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" ng-disabled="deepSyncInProgress">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu" ng-controller="ThemesListController">
<a class="dropdown-item" ng-click="deepSync(theme)" ng-repeat="theme in themes" ng-bind="theme.name"></a>
</div>
</div>
<button type="button" class="btn btn-secondary" ng-click="speedyDeepSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Synchronisation sans fichiers</button>
<a href="check_import.html" class="btn btn-success" target="_self" ng-if="syncReport">Voir le rapport</a>
<div class="row mb-2" ng-show="config.wip || !timeProgression || displayDangerousActions">
<div class="d-flex flex-column">
<a href="sync" class="btn btn-secondary my-1" type="button"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> Paramètres de synchronisation</a>
<button ng-click="reset('settings');" class="btn btn-secondary mt-2 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Revenir aux paramètres par défaut</button>
<button ng-click="reset('challenges')" class="btn btn-secondary mt-2 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer les challenges et les thèmes</button>
<button ng-click="reset('teams');" class="btn btn-secondary mt-1 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer les équipes</button>
<button ng-click="reset('game');" class="btn btn-secondary mt-1 mb-2" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer la partie (tentatives, indices, ...)</button>
</div>
</div>
</div>
<div class="col-4 d-flex flex-column">
<button ng-click="reset('settings');" class="btn btn-secondary mt-2 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Revenir aux paramètres par défaut</button>
<button ng-click="reset('challenges')" class="btn btn-secondary mt-2 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer les challenges et les thèmes</button>
<button ng-click="reset('teams');" class="btn btn-secondary mt-1 mb-1" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer les équipes</button>
<button ng-click="reset('game');" class="btn btn-secondary mt-1 mb-2" type="button"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Effacer la partie (tentatives, indices, ...)</button>
</div>
</div>

View File

@ -0,0 +1,47 @@
<div class="card mt-3 mb-5" ng-show="!config.wip && timeProgression && !displayDangerousActions">
<div class="card-header bg-secondary text-light">
<h3 class="mb-0">
Synchronisation
</h3>
</div>
<div class="card-body">
<div class="form-check">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="displayDangerousActions">
<strong class="custom-control-label">Je sais ce que le challenge a démarré ET <span style="text-decoration: underline red;">j'ai réalisé une sauvegarde de la base de données il y a moins d'une minute</span> ET je sais que c'est <span style="text-decoration: underline yellow;">une très mauvaise idée de cocher cette case</span>, mais j'y suis obligé pour de bonnes raisons.</strong>
</label>
</div>
</div>
</div>
<div class="card mt-3 mb-5 text-light bg-dark" ng-show="config.wip || !timeProgression || displayDangerousActions">
<div class="card-body">
<dl class="row">
<dt class="col-2">Synchronisation</dt>
<dd class="col-10" title="{{ configro['sync-type'] }}" ng-bind="configro.sync"></dd>
<dt class="col-2" ng-if="configro['sync-id']">ID</dt>
<dd class="col-10" ng-if="configro['sync-id']">{{ configro['sync-id'] }}</dd>
</dl>
<div class="float-right" ng-if="configro.sync">
{{ syncProgress }}
</div>
<div ng-if="syncReport">
Dernier import&nbsp;: {{ syncReport._date[1] }}
</div>
<div class="text-left" ng-if="configro.sync">
<button ng-if="configro['sync-type'] === 'GitImporter'" type="button" class="btn btn-info" ng-click="baseSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-repeat" aria-hidden="true"></span> Pull</button>
<div class="btn-group">
<button type="button" class="btn btn-secondary" ng-click="deepSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Synchronisation intégrale</button>
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" ng-disabled="deepSyncInProgress">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu" ng-controller="ThemesListController">
<a class="dropdown-item" ng-click="deepSync(theme)" ng-repeat="theme in themes" ng-bind="theme.name"></a>
</div>
</div>
<button type="button" class="btn btn-secondary" ng-click="speedyDeepSync()" ng-disabled="deepSyncInProgress"><span class="glyphicon glyphicon-import" aria-hidden="true"></span> Synchronisation sans fichiers</button>
<a href="check_import.html" class="btn btn-success" target="_self" ng-if="syncReport">Voir le rapport</a>
</div>
</div>
</div>