admin: improve claims with related exercices

This commit is contained in:
nemunaire 2020-01-20 10:41:21 +01:00
parent 2e3f7c6894
commit 32dc9c1a8c
5 changed files with 91 additions and 11 deletions

View File

@ -2,6 +2,7 @@ package api
import ( import (
"encoding/json" "encoding/json"
"errors"
"time" "time"
"srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/libfic"
@ -11,9 +12,12 @@ import (
func init() { func init() {
// Tasks // Tasks
router.GET("/api/claims/", apiHandler(getClaims)) router.GET("/api/claims", apiHandler(getClaims))
router.POST("/api/claims/", apiHandler(newClaim)) router.POST("/api/claims", apiHandler(newClaim))
router.DELETE("/api/claims/", apiHandler(clearClaims)) router.DELETE("/api/claims", apiHandler(clearClaims))
router.GET("/api/teams/:tid/claims", apiHandler(teamHandler(getTeamClaims)))
router.GET("/api/exercices/:eid/claims", apiHandler(exerciceHandler(getExerciceClaims)))
router.GET("/api/themes/:thid/exercices/:eid/claims", apiHandler(exerciceHandler(getExerciceClaims)))
router.GET("/api/claims/:cid", apiHandler(claimHandler(showClaim))) router.GET("/api/claims/:cid", apiHandler(claimHandler(showClaim)))
router.PUT("/api/claims/:cid", apiHandler(claimHandler(updateClaim))) router.PUT("/api/claims/:cid", apiHandler(claimHandler(updateClaim)))
@ -21,8 +25,8 @@ func init() {
router.DELETE("/api/claims/:cid", apiHandler(claimHandler(deleteClaim))) router.DELETE("/api/claims/:cid", apiHandler(claimHandler(deleteClaim)))
// Assignees // Assignees
router.GET("/api/claims-assignees/", apiHandler(getAssignees)) router.GET("/api/claims-assignees", apiHandler(getAssignees))
router.POST("/api/claims-assignees/", apiHandler(newAssignee)) router.POST("/api/claims-assignees", apiHandler(newAssignee))
router.GET("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(showClaimAssignee))) router.GET("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(showClaimAssignee)))
router.PUT("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(updateClaimAssignee))) router.PUT("/api/claims-assignees/:aid", apiHandler(claimAssigneeHandler(updateClaimAssignee)))
@ -33,6 +37,14 @@ func getClaims(_ httprouter.Params, _ []byte) (interface{}, error) {
return fic.GetClaims() return fic.GetClaims()
} }
func getTeamClaims(team fic.Team, _ []byte) (interface{}, error) {
return team.GetClaims()
}
func getExerciceClaims(exercice fic.Exercice, _ []byte) (interface{}, error) {
return exercice.GetClaims()
}
type ClaimExported struct { type ClaimExported struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Subject string `json:"subject"` Subject string `json:"subject"`
@ -97,6 +109,10 @@ func newClaim(_ httprouter.Params, body []byte) (interface{}, error) {
return nil, err return nil, err
} }
if uc.Subject == "" {
return nil, errors.New("Claim's subject cannot be empty.")
}
var t *fic.Team var t *fic.Team
if uc.Team != nil { if uc.Team != nil {
if team, err := fic.GetTeam(*uc.Team); err != nil { if team, err := fic.GetTeam(*uc.Team); err != nil {
@ -130,6 +146,10 @@ func newClaim(_ httprouter.Params, body []byte) (interface{}, error) {
a = nil a = nil
} }
if uc.Priority == "" {
uc.Priority = "medium"
}
return fic.NewClaim(uc.Subject, t, e, a, uc.Priority) return fic.NewClaim(uc.Subject, t, e, a, uc.Priority)
} }

View File

@ -229,6 +229,9 @@ angular.module("FICApp")
patch: {method: 'PATCH'} patch: {method: 'PATCH'}
}) })
}) })
.factory("ExerciceClaims", function($resource) {
return $resource("/api/exercices/:exerciceId/claims", { exerciceId: '@idExercice'})
})
.factory("ExerciceTags", function($resource) { .factory("ExerciceTags", function($resource) {
return $resource("/api/exercices/:exerciceId/tags", { exerciceId: '@idExercice'}, { return $resource("/api/exercices/:exerciceId/tags", { exerciceId: '@idExercice'}, {
update: {method: 'PUT'} update: {method: 'PUT'}
@ -267,7 +270,7 @@ angular.module("FICApp")
} }
}) })
.filter("toColor", function() { .filter("toColor", function() {
return function(input) { return function(num) {
num >>>= 0; num >>>= 0;
var b = num & 0xFF, var b = num & 0xFF,
g = (num & 0xFF00) >>> 8, g = (num & 0xFF00) >>> 8,
@ -1056,14 +1059,14 @@ angular.module("FICApp")
$location.url("/claims/" + id); $location.url("/claims/" + id);
}; };
}) })
.controller("ClaimController", function($scope, Claim, ClaimAssignee, Teams, Exercice, $routeParams, $location, $http) { .controller("ClaimController", function($scope, Claim, ClaimAssignee, Teams, Exercice, $routeParams, $location, $http, $rootScope) {
$scope.claim = Claim.get({ claimId: $routeParams.claimId }, function(v) { $scope.claim = Claim.get({ claimId: $routeParams.claimId }, function(v) {
v.id_team = "" + v.id_team; v.id_team = "" + v.id_team;
if (!v.priority) if (!v.priority)
v.priority = "medium"; v.priority = "medium";
}); });
if ($routeParams.claimId == "new") if ($routeParams.claimId == "new")
$scope.fields = ["id_team", "subject", "priority", "id_exercice", "id_assignee"]; $scope.fields = ["id_team", "id_exercice", "subject", "priority", "id_assignee"];
else else
$scope.fields = ["state", "subject", "priority", "id_exercice", "id_assignee", "id_team", "creation"]; $scope.fields = ["state", "subject", "priority", "id_exercice", "id_assignee", "id_team", "creation"];
$scope.assignees = ClaimAssignee.query(); $scope.assignees = ClaimAssignee.query();
@ -1132,6 +1135,8 @@ angular.module("FICApp")
if (!this.ndescription) if (!this.ndescription)
this.ndescription = "Création de la tâche"; this.ndescription = "Création de la tâche";
$scope.saveDescription(); $scope.saveDescription();
}, function(response) {
$rootScope.newBox('danger', 'An error occurs when trying to save claim:', response.data.errmsg);
}); });
} }
} }
@ -1362,6 +1367,22 @@ angular.module("FICApp")
} }
}) })
.controller("ExerciceClaimsController", function($scope, ExerciceClaims, Team, ClaimAssignee, $routeParams, $location) {
$scope.claims = ExerciceClaims.query({ exerciceId: $routeParams.exerciceId });
$scope.assignees = ClaimAssignee.query();
$scope.claims.$promise.then(function(claims) {
claims.forEach(function(claim, cid) {
$scope.claims[cid].team = Team.get({ teamId: claim.id_team });
})
});
$scope.showClosed = false;
$scope.show = function(id) {
$location.url("/claims/" + id);
};
})
.controller("ExerciceTagsController", function($scope, ExerciceTags, $routeParams, $rootScope) { .controller("ExerciceTagsController", function($scope, ExerciceTags, $routeParams, $rootScope) {
$scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId }); $scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId });

View File

@ -9,8 +9,13 @@
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="claim[field]" ng-if="field != 'state' && field != 'priority' && field != 'creation' && field != 'id_team' && field != 'id_exercice' && field != 'id_assignee' && field != 'description'"> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="claim[field]" ng-if="field != 'state' && field != 'priority' && field != 'creation' && field != 'id_team' && field != 'id_exercice' && field != 'id_assignee' && field != 'description'">
<input type="datetime" class="form-control form-control-sm" id="{{ field }}" ng-model="claim[field]" ng-if="field == 'creation' && claim.id"> <input type="datetime" class="form-control form-control-sm" id="{{ field }}" ng-model="claim[field]" ng-if="field == 'creation' && claim.id">
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as v for (k, v) in states" ng-if="field == 'state'"></select> <select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as v for (k, v) in states" ng-if="field == 'state'"></select>
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as v for (k, v) in priorities" ng-if="field == 'priority'"><option value="medium">Par défaut</option></select> <select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as v for (k, v) in priorities" ng-if="field == 'priority'"></select>
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'id_exercice'"><option></option></select> <div class="input-group" ng-if="field == 'id_exercice'">
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices"><option></option></select>
<div class="input-group-append" ng-if="claim.id_exercice">
<a type="button" class="btn btn-sm btn-outline-secondary" ng-href="exercices/{{ claim.id_exercice }}">Aller au challenge</a>
</div>
</div>
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as t.name for (k, t) in teams" ng-if="field == 'id_team' && !claim.id"><option></option></select> <select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as t.name for (k, t) in teams" ng-if="field == 'id_team' && !claim.id"><option></option></select>
<div class="input-group" ng-if="field == 'id_team' && claim.id"> <div class="input-group" ng-if="field == 'id_team' && claim.id">
<select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as t.name for (k, t) in teams"><option></option></select> <select class="custom-select custom-select-sm" id="{{ field }}" ng-model="claim[field]" ng-options="k as t.name for (k, t) in teams"><option></option></select>

View File

@ -35,6 +35,20 @@
</form> </form>
<div class="col-md-4 accordion" ng-show="exercice.id" id="accordionExercice"> <div class="col-md-4 accordion" ng-show="exercice.id" id="accordionExercice">
<div class="mb-2" style="overflow-y: auto; max-height: 250px" ng-controller="ExerciceClaimsController" ng-show="claims.length">
<table class="table table-sm table-hover table-striped">
<tbody>
<tr ng-repeat="claim in claims | orderBy: 'priority'" ng-click="show(claim.id)" ng-class="{'table-info': claim.priority == 'medium', 'table-warning': claim.priority == 'high', 'table-danger': claim.priority == 'critical'}" ng-if="showClosed || (claim.state != 'closed' && claim.state != 'invalid')">
<td ng-bind="claim.subject"></td>
<td><a ng-href="teams/{{ claim.id_team}}" style="background-color: {{ claim.team.color | toColor }}" ng-bind="claim.team.name"></a></td>
<td ng-bind="claim.state"></td>
<td ng-repeat="assignee in assignees" ng-if="assignee.id == claim.id_assignee" ng-bind="assignee.name"></td>
</tr>
</tbody>
</table>
</div>
<div class="card border-secondary" ng-controller="ExerciceFilesController"> <div class="card border-secondary" ng-controller="ExerciceFilesController">
<div class="card-header bg-secondary text-light" type="button" data-toggle="collapse" data-target="#collapseFiles" aria-expanded="true" aria-controls="collapseFiles"> <div class="card-header bg-secondary text-light" type="button" data-toggle="collapse" data-target="#collapseFiles" aria-expanded="true" aria-controls="collapseFiles">
<h4 class="m-0"><small class="glyphicon glyphicon-chevron-right" aria-hidden="true"></small> Téléchargements</h4> <h4 class="m-0"><small class="glyphicon glyphicon-chevron-right" aria-hidden="true"></small> Téléchargements</h4>

View File

@ -46,7 +46,27 @@ func GetClaims() (res []Claim, err error) {
// GetClaims returns a list of all Claim registered for the Team. // GetClaims returns a list of all Claim registered for the Team.
func (t Team) GetClaims() (res []Claim, err error) { func (t Team) GetClaims() (res []Claim, err error) {
var rows *sql.Rows var rows *sql.Rows
if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE IdTeam = ?", t.Id); err != nil { if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_team = ?", t.Id); err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var c Claim
if err = rows.Scan(&c.Id, &c.Subject, &c.IdTeam, &c.IdExercice, &c.IdAssignee, &c.Creation, &c.State, &c.Priority); err != nil {
return
}
res = append(res, c)
}
err = rows.Err()
return
}
// GetExercices returns a list of all Claim registered for the Exercice.
func (e Exercice) GetClaims() (res []Claim, err error) {
var rows *sql.Rows
if rows, err = DBQuery("SELECT id_claim, subject, id_team, id_exercice, id_assignee, creation, state, priority FROM claims WHERE id_exercice = ?", e.Id); err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()