[admin] Improve interface
This commit is contained in:
parent
017adfb2b1
commit
347f317dd9
7 changed files with 421 additions and 13 deletions
|
@ -17,34 +17,90 @@ angular.module("FICApp", ["ngRoute", "ngResource"])
|
|||
controller: "TeamsListController",
|
||||
templateUrl: "views/team-list.html"
|
||||
})
|
||||
.when("/teams/:teamId", {
|
||||
controller: "TeamController",
|
||||
templateUrl: "views/team.html"
|
||||
})
|
||||
.when("/teams/new", {
|
||||
controller: "TeamNewController",
|
||||
templateUrl: "views/team-new.html"
|
||||
})
|
||||
.when("/", {
|
||||
templateUrl: "views/home.html"
|
||||
});
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
angular.module("FICApp")
|
||||
.factory("Version", function($resource) {
|
||||
return $resource("/api/version")
|
||||
})
|
||||
.factory("Team", function($resource) {
|
||||
return $resource("/api/teams/:teamId", { teamId: '@id' }, {
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
});
|
||||
angular.module("FICApp")
|
||||
})
|
||||
.factory("TeamMember", function($resource) {
|
||||
return $resource("/api/teams/:teamId/members", { teamId: '@id' })
|
||||
})
|
||||
.factory("TeamMy", function($resource) {
|
||||
return $resource("/api/teams/:teamId/my.json", { teamId: '@id' })
|
||||
})
|
||||
.factory("Teams", function($resource) {
|
||||
return $resource("/api/teams/teams.json")
|
||||
})
|
||||
.factory("TeamStats", function($resource) {
|
||||
return $resource("/api/teams/:teamId/stats.json", { teamId: '@id' })
|
||||
})
|
||||
.factory("TeamPresence", function($resource) {
|
||||
return $resource("/api/teams/:teamId/tries", { teamId: '@id' })
|
||||
})
|
||||
.factory("Theme", function($resource) {
|
||||
return $resource("/api/themes/:themeId", null, {
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
});
|
||||
angular.module("FICApp")
|
||||
.factory("Exercice", function($resource) {
|
||||
return $resource("/api/themes/:themeId/:exerciceId", null, {
|
||||
'query': {method: "GET", url: "/api/themes/:themeId/exercices", isArray: true},
|
||||
'save': {method: 'PATCH'},
|
||||
})
|
||||
.factory("Themes", function($resource) {
|
||||
return $resource("/api/themes/themes.json", null, {
|
||||
'get': {method: 'GET'},
|
||||
})
|
||||
})
|
||||
.factory("Exercice", function($resource) {
|
||||
return $resource("/api/exercices/:exerciceId")
|
||||
});
|
||||
|
||||
String.prototype.capitalize = function() {
|
||||
return this
|
||||
.toLowerCase()
|
||||
.replace(
|
||||
/(^|\s)([a-z])/g,
|
||||
function(m,p1,p2) { return p1+p2.toUpperCase(); }
|
||||
);
|
||||
}
|
||||
|
||||
angular.module("FICApp")
|
||||
.filter("capitalize", function() {
|
||||
return function(input) {
|
||||
if (input != undefined)
|
||||
return input.capitalize();
|
||||
}
|
||||
})
|
||||
.filter("time", function() {
|
||||
return function(input) {
|
||||
if (input == undefined) {
|
||||
return "--";
|
||||
} else if (input >= 10) {
|
||||
return input;
|
||||
} else {
|
||||
return "0" + input;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.controller("VersionController", function($scope, Version) {
|
||||
$scope.v = Version.get();
|
||||
})
|
||||
|
||||
.controller("ThemesListController", function($scope, Theme, $location) {
|
||||
$scope.themes = Theme.query();
|
||||
$scope.fields = ["id", "name"];
|
||||
|
@ -81,14 +137,278 @@ angular.module("FICApp")
|
|||
|
||||
.controller("TeamsListController", function($scope, Team, $location) {
|
||||
$scope.teams = Team.query();
|
||||
$scope.fields = ["id", "name"];
|
||||
$scope.fields = ["id", "name", "initialName"];
|
||||
|
||||
$scope.show = function(id) {
|
||||
$location.url("/teams/" + id);
|
||||
};
|
||||
})
|
||||
.controller("TeamController", function($scope, Team, TeamMember, $routeParams) {
|
||||
$scope.team = Team.get({ teamId: $routeParams.teamId });
|
||||
$scope.members = TeamMember.query({ teamId: $routeParams.teamId });
|
||||
})
|
||||
.controller("TeamStatsController", function($scope, TeamStats, $routeParams) {
|
||||
$scope.teamstats = TeamStats.get({ teamId: $routeParams.teamId });
|
||||
$scope.teamstats.$promise.then(function(res) {
|
||||
solvedByLevelPie("#pieLevels", res.levels);
|
||||
solvedByThemesPie("#pieThemes", res.themes);
|
||||
});
|
||||
})
|
||||
.controller("TeamExercicesController", function($scope, Teams, Themes, TeamMy, Exercice, $routeParams) {
|
||||
$scope.teams = Teams.get();
|
||||
$scope.themes = Themes.get();
|
||||
$scope.exercices = Exercice.query();
|
||||
$scope.my = TeamMy.get({ teamId: $routeParams.teamId });
|
||||
|
||||
$scope.teams.$promise.then(function(res){
|
||||
$scope.nb_teams = 0;
|
||||
$scope.nb_reg_teams = Object.keys(res).length;
|
||||
angular.forEach(res, function(team, tid) {
|
||||
if (team.rank)
|
||||
$scope.nb_teams += 1;
|
||||
}, 0);
|
||||
});
|
||||
|
||||
$scope.my.$promise.then(function(res){
|
||||
$scope.solved_exercices = 0;
|
||||
angular.forEach(res.exercices, function(exercice, eid) {
|
||||
if (exercice.solved) {
|
||||
$scope.solved_exercices += 1;
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
|
||||
})
|
||||
.controller("TeamNewController", function($scope, Team, $location) {
|
||||
$scope.contact = new Team({
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
.controller("PresenceController", function($scope, TeamPresence, $routeParams) {
|
||||
$scope.presence = TeamPresence.query({ teamId: $routeParams.teamId });
|
||||
$scope.presence.$promise.then(function(res) {
|
||||
presenceCal("#presenceCal", res);
|
||||
});
|
||||
})
|
||||
.controller("CountdownController", function($scope, $http, $timeout) {
|
||||
$scope.time = {};
|
||||
function updTime() {
|
||||
$timeout.cancel($scope.cbm);
|
||||
$scope.cbm = $timeout(updTime, 1000);
|
||||
if (sessionStorage.userService) {
|
||||
var time = angular.fromJson(sessionStorage.userService);
|
||||
var srv_cur = (Date.now() + (time.cu * 1000 - time.he)) / 1000;
|
||||
var remain = time.du;
|
||||
if (time.st == Math.floor(srv_cur)) {
|
||||
$scope.refresh(true);
|
||||
}
|
||||
if (time.st > 0 && time.st <= srv_cur) {
|
||||
remain = time.st + time.du - srv_cur;
|
||||
}
|
||||
if (remain < 0) {
|
||||
remain = 0;
|
||||
$scope.time.end = true;
|
||||
$scope.time.expired = true;
|
||||
} else if (remain < 60) {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = true;
|
||||
} else {
|
||||
$scope.time.end = false;
|
||||
$scope.time.expired = false;
|
||||
}
|
||||
$scope.time.start = time.st * 1000;
|
||||
$scope.time.duration = time.du * 1000;
|
||||
$scope.time.remaining = remain;
|
||||
$scope.time.hours = Math.floor(remain / 3600);
|
||||
$scope.time.minutes = Math.floor((remain % 3600) / 60);
|
||||
$scope.time.seconds = Math.floor(remain % 60);
|
||||
}
|
||||
}
|
||||
|
||||
$http.get("/time.json").success(function(time) {
|
||||
time.he = (new Date()).getTime();
|
||||
sessionStorage.userService = angular.toJson(time);
|
||||
updTime();
|
||||
});
|
||||
});
|
||||
|
||||
function solvedByLevelPie(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width - 10,
|
||||
height = d3.select(location).node().getBoundingClientRect().width - 10,
|
||||
radius = Math.min(width, height) / 2,
|
||||
innerRadius = 0.1 * radius;
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.width; });
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(function (d) {
|
||||
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
||||
});
|
||||
|
||||
var outlineArc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(radius);
|
||||
|
||||
var svg = d3.select(location).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.score = d.solved * 100 / d.total;
|
||||
d.width = d.tries + 1;
|
||||
});
|
||||
|
||||
var path = svg.selectAll(".solidArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d) { return color(d.data.tip); })
|
||||
.attr("class", "solidArc")
|
||||
.attr("stroke", "gray")
|
||||
.attr("d", arc);
|
||||
|
||||
var outerPath = svg.selectAll(".outlineArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "gray")
|
||||
.attr("class", "outlineArc")
|
||||
.attr("d", outlineArc);
|
||||
|
||||
var labelArc = d3.svg.arc()
|
||||
.outerRadius(0.8 * radius)
|
||||
.innerRadius(0.8 * radius);
|
||||
|
||||
svg.selectAll(".labelArc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tip + ": " + d.data.solved + "/" + d.data.total; });
|
||||
|
||||
svg.selectAll(".label2Arc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tries; });
|
||||
}
|
||||
|
||||
function solvedByThemesPie(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width,
|
||||
height = d3.select(location).node().getBoundingClientRect().width,
|
||||
radius = Math.min(width, height) / 2,
|
||||
innerRadius = 0.1 * radius;
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.range(["#9E0041", "#C32F4B", "#E1514B", "#F47245", "#FB9F59", "#FEC574", "#FAE38C", "#EAD195", "#C7E89E", "#9CD6A4", "#6CC4A4", "#4D9DB4", "#4776B4", "#5E4EA1"]);
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.width; });
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(function (d) {
|
||||
return (radius - innerRadius) * (d.data.score / 100.0) + innerRadius;
|
||||
});
|
||||
|
||||
var outlineArc = d3.svg.arc()
|
||||
.innerRadius(innerRadius)
|
||||
.outerRadius(radius);
|
||||
|
||||
var svg = d3.select(location).append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.score = d.solved * 100 / d.total;
|
||||
d.width = d.tries + 0.5;
|
||||
});
|
||||
|
||||
var path = svg.selectAll(".solidArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", function(d) { return color(d.data.tip); })
|
||||
.attr("class", "solidArc")
|
||||
.attr("stroke", "gray")
|
||||
.attr("d", arc);
|
||||
|
||||
var outerPath = svg.selectAll(".outlineArc")
|
||||
.data(pie(data))
|
||||
.enter().append("path")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "gray")
|
||||
.attr("class", "outlineArc")
|
||||
.attr("d", outlineArc);
|
||||
|
||||
svg.selectAll(".label2Arc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.solved; });
|
||||
|
||||
var labelArc = d3.svg.arc()
|
||||
.outerRadius(0.8 * radius)
|
||||
.innerRadius(0.8 * radius);
|
||||
|
||||
svg.selectAll(".labelArc")
|
||||
.data(pie(data))
|
||||
.enter().append("text")
|
||||
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(function(d) { return d.data.tip + ": " + d.data.tries; });
|
||||
}
|
||||
|
||||
function presenceCal(location, data) {
|
||||
var width = d3.select(location).node().getBoundingClientRect().width,
|
||||
height = 80,
|
||||
cellSize = 17; // cell size
|
||||
|
||||
var percent = d3.format(".1%"),
|
||||
format = d3.time.format("%H:%M");
|
||||
|
||||
var color = d3.scale.quantize()
|
||||
.domain([0, 16])
|
||||
.range(d3.range(8).map(function(d) { return "q" + d + "-8"; }));
|
||||
|
||||
var svg = d3.select(location).selectAll("svg")
|
||||
.data(d3.range(26, 29))
|
||||
.enter().append("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("class", "RdYlGn")
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + ((width - cellSize * 24) / 2) + "," + (height - cellSize * 4 - 1) + ")");
|
||||
|
||||
svg.append("text")
|
||||
.attr("transform", "translate(-6," + cellSize * 2.6 + ")rotate(-90)")
|
||||
.style("text-anchor", "middle")
|
||||
.text(function(d) { return d + "-02"; });
|
||||
|
||||
var rect = svg.selectAll(".quarter")
|
||||
.data(function(d) { return d3.time.minutes(new Date(2016, 1, d, 0), new Date(2016, 1, d, 24), 15); })
|
||||
.enter().append("rect")
|
||||
.attr("width", cellSize)
|
||||
.attr("height", cellSize)
|
||||
.attr("class", function(d) { return color(data.reduce(function(prev, cur){
|
||||
cur = new Date(cur).getTime();
|
||||
dv = d.getTime();
|
||||
return prev + ((dv <= cur && cur < dv+15*60000)?1:0);
|
||||
}, 0)); });
|
||||
}
|
||||
|
|
Reference in a new issue