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

View File

@ -229,6 +229,9 @@ angular.module("FICApp")
patch: {method: 'PATCH'}
})
})
.factory("ExerciceClaims", function($resource) {
return $resource("/api/exercices/:exerciceId/claims", { exerciceId: '@idExercice'})
})
.factory("ExerciceTags", function($resource) {
return $resource("/api/exercices/:exerciceId/tags", { exerciceId: '@idExercice'}, {
update: {method: 'PUT'}
@ -267,7 +270,7 @@ angular.module("FICApp")
}
})
.filter("toColor", function() {
return function(input) {
return function(num) {
num >>>= 0;
var b = num & 0xFF,
g = (num & 0xFF00) >>> 8,
@ -1056,14 +1059,14 @@ angular.module("FICApp")
$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) {
v.id_team = "" + v.id_team;
if (!v.priority)
v.priority = "medium";
});
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
$scope.fields = ["state", "subject", "priority", "id_exercice", "id_assignee", "id_team", "creation"];
$scope.assignees = ClaimAssignee.query();
@ -1132,6 +1135,8 @@ angular.module("FICApp")
if (!this.ndescription)
this.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);
});
}
}
@ -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) {
$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="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 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="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'id_exercice'"><option></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>
<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>
<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>

View File

@ -35,6 +35,20 @@
</form>
<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-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>

View File

@ -46,7 +46,27 @@ func GetClaims() (res []Claim, err error) {
// GetClaims returns a list of all Claim registered for the Team.
func (t Team) 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 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
}
defer rows.Close()