done. ready!

This commit is contained in:
nemunaire 2018-07-20 05:04:06 +02:00
parent a517a2201f
commit b3abc0d434
12 changed files with 326 additions and 111 deletions

View File

@ -11,14 +11,17 @@ import (
) )
func init() { func init() {
router.GET("/api/checks/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.GET("/api/checks", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.GetChecks() return ckh.GetChecks()
})) }))
router.DELETE("/api/checks", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.ClearChecks()
}))
router.GET("/api/items/:iid/checks/", apiHandler(itemHandler(func (item ckh.Item, _ []byte) (interface{}, error) { router.GET("/api/items/:iid/checks", apiHandler(itemHandler(func (item ckh.Item, _ []byte) (interface{}, error) {
return item.GetChecks() return item.GetChecks()
}))) })))
router.POST("/api/items/:iid/checks/", apiHandler(itemHandler(newCheck))) router.POST("/api/items/:iid/checks", apiHandler(itemHandler(newCheck)))
router.GET("/api/items/:iid/checks/:cid", apiHandler(checkHandler(func (check ckh.Check, _ ckh.Item, _ []byte) (interface{}, error) { router.GET("/api/items/:iid/checks/:cid", apiHandler(checkHandler(func (check ckh.Check, _ ckh.Item, _ []byte) (interface{}, error) {
return check, nil return check, nil
@ -29,10 +32,10 @@ func init() {
}))) })))
// in room // in room
router.GET("/api/rooms/:rid/items/:iid/checks/", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) { router.GET("/api/rooms/:rid/items/:iid/checks", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) {
return item.GetChecks() return item.GetChecks()
}))) })))
router.POST("/api/rooms/:rid/items/:iid/checks/", apiHandler(itemInRoomHandler(newCheckInRoom))) router.POST("/api/rooms/:rid/items/:iid/checks", apiHandler(itemInRoomHandler(newCheckInRoom)))
router.GET("/api/rooms/:rid/items/:iid/checks/:cid", apiHandler(checkInRoomHandler(func (check ckh.Check, _ ckh.Item, _ []byte) (interface{}, error) { router.GET("/api/rooms/:rid/items/:iid/checks/:cid", apiHandler(checkInRoomHandler(func (check ckh.Check, _ ckh.Item, _ []byte) (interface{}, error) {
return check, nil return check, nil

View File

@ -11,30 +11,64 @@ import (
) )
func init() { func init() {
router.GET("/api/items/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.GET("/api/items", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.GetItems() return ckh.GetItems()
})) }))
router.DELETE("/api/items/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.DELETE("/api/items", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.ClearItems() return ckh.ClearItems()
})) }))
router.GET("/api/rooms/:rid/items/", apiHandler(roomHandler(func (room ckh.Room, _ []byte) (interface{}, error) { router.GET("/api/rooms/:rid/items", apiHandler(roomHandler(func (room ckh.Room, _ []byte) (interface{}, error) {
return room.GetItems() if items, err := room.GetItems(); err != nil {
return nil, err
} else {
var res []item
for _, item := range items {
if i, err := displayItem(item); err != nil {
return nil, err
} else {
res = append(res, i)
}
}
return res, nil
}
}))) })))
router.POST("/api/rooms/:rid/items/", apiHandler(roomHandler(newItem))) router.POST("/api/rooms/:rid/items", apiHandler(roomHandler(newItem)))
router.DELETE("/api/rooms/:rid/items/", apiHandler(roomHandler(func (room ckh.Room, _ []byte) (interface{}, error) { router.DELETE("/api/rooms/:rid/items", apiHandler(roomHandler(func (room ckh.Room, _ []byte) (interface{}, error) {
return room.ClearItems() return room.ClearItems()
}))) })))
router.GET("/api/rooms/:rid/items/:iid/", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) { router.GET("/api/rooms/:rid/items/:iid", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) {
return item, nil return displayItem(item)
}))) })))
router.PUT("/api/rooms/:rid/items/:iid/", apiHandler(itemInRoomHandler(updateItem))) router.PUT("/api/rooms/:rid/items/:iid", apiHandler(itemInRoomHandler(updateItem)))
router.DELETE("/api/rooms/:rid/items/:iid/", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) { router.DELETE("/api/rooms/:rid/items/:iid", apiHandler(itemInRoomHandler(func (item ckh.Item, _ ckh.Room, _ []byte) (interface{}, error) {
return item.Delete() return item.Delete()
}))) })))
} }
type item struct {
Id int64 `json:"id"`
Label string `json:"label"`
Description string `json:"description"`
Tags []string `json:"tags"`
}
func displayItem(item ckh.Item) (i item, err error) {
var tags []ckh.Tag
if tags, err = item.GetTags(); err != nil {
return
} else {
for _, t := range tags {
i.Tags = append(i.Tags, t.Label)
}
}
i.Id = item.Id
i.Label = item.Label
i.Description = item.Description
return
}
func itemHandler(f func(ckh.Item, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) { func itemHandler(f func(ckh.Item, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
return func(ps httprouter.Params, body []byte) (interface{}, error) { return func(ps httprouter.Params, body []byte) (interface{}, error) {
if iid, err := strconv.ParseInt(string(ps.ByName("iid")), 10, 64); err != nil { if iid, err := strconv.ParseInt(string(ps.ByName("iid")), 10, 64); err != nil {

View File

@ -11,11 +11,11 @@ import (
) )
func init() { func init() {
router.GET("/api/rooms/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.GET("/api/rooms", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.GetRooms() return ckh.GetRooms()
})) }))
router.POST("/api/rooms/", apiHandler(newRoom)) router.POST("/api/rooms", apiHandler(newRoom))
router.DELETE("/api/rooms/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.DELETE("/api/rooms", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.ClearRooms() return ckh.ClearRooms()
})) }))

View File

@ -11,11 +11,11 @@ import (
) )
func init() { func init() {
router.GET("/api/tags/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.GET("/api/tags", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.GetTags() return ckh.GetTags()
})) }))
router.POST("/api/tags/", apiHandler(newTag)) router.POST("/api/tags", apiHandler(newTag))
router.DELETE("/api/tags/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.DELETE("/api/tags", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.ClearTags() return ckh.ClearTags()
})) }))
@ -35,6 +35,9 @@ func init() {
router.GET("/api/items/:iid/tags", apiHandler(itemHandler(func (item ckh.Item, _ []byte) (interface{}, error) { router.GET("/api/items/:iid/tags", apiHandler(itemHandler(func (item ckh.Item, _ []byte) (interface{}, error) {
return item.GetTags() return item.GetTags()
}))) })))
router.DELETE("/api/items/:iid/tags", apiHandler(itemHandler(func (item ckh.Item, _ []byte) (interface{}, error) {
return item.ClearItemTags()
})))
router.PUT("/api/items/:iid/tags/:tid", apiHandler(itemTagHandler(func (item ckh.Item, tag ckh.Tag, _ []byte) (interface{}, error) { router.PUT("/api/items/:iid/tags/:tid", apiHandler(itemTagHandler(func (item ckh.Item, tag ckh.Tag, _ []byte) (interface{}, error) {
_, err := tag.AddItem(item) _, err := tag.AddItem(item)
return tag, err return tag, err

View File

@ -11,11 +11,11 @@ import (
) )
func init() { func init() {
router.GET("/api/users/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.GET("/api/users", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.GetUsers() return ckh.GetUsers()
})) }))
router.POST("/api/users/", apiHandler(newUser)) router.POST("/api/users", apiHandler(newUser))
router.DELETE("/api/users/", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) { router.DELETE("/api/users", apiHandler(func (_ httprouter.Params, _ []byte) (interface{}, error) {
return ckh.ClearUsers() return ckh.ClearUsers()
})) }))

View File

@ -2,18 +2,36 @@
<html ng-app="CheckHomeApp"> <html ng-app="CheckHomeApp">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Terrasses de la Bièvre - remise des clefs</title> <title>Terrasses de la Bièvre - remise des clefs</title>
<link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet"> <link href="/css/bootstrap.min.css" type="text/css" rel="stylesheet">
<link href="/css/glyphicon.css" type="text/css" rel="stylesheet" media="screen"> <link href="/css/glyphicon.css" type="text/css" rel="stylesheet" media="screen">
<base href="/"> <base href="/">
</head> </head>
<body> <body>
<nav class="nav"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-2">
<a class="nav-link" href="/">Accueil</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<a class="nav-link" href="/items">Éléments</a> <span class="navbar-toggler-icon"></span>
<a class="nav-link" href="/rooms">Pièces</a> </button>
<a class="nav-link" href="/tags">Étiquettes</a> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<a class="nav-link" href="/users">Utilisateurs</a> <ul class="navbar-nav mr-auto">
<li class="nav-item" ng-class="{'active': currecurrentPage == '' || currentPage == 'home'}">
<a class="nav-link" href="/"><span class="glyphicon glyphicon-home" aria-hidden="true"></span> Accueil</a>
</li>
<li class="nav-item" ng-class="{'active': currentPage == 'items'}">
<a class="nav-link" href="/items"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Éléments</a>
</li>
<li class="nav-item" ng-class="{'active': currentPage == 'rooms'}">
<a class="nav-link" href="/rooms"><span class="glyphicon glyphicon-tower" aria-hidden="true"></span> Pièces</a>
</li>
<li class="nav-item" ng-class="{'active': currentPage == 'tags'}">
<a class="nav-link" href="/tags"><span class="glyphicon glyphicon-tags" aria-hidden="true"></span> Étiquettes</a>
</li>
<li class="nav-item" ng-class="{'active': currentPage == 'users'}">
<a class="nav-link" href="/users"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Utilisateurs</a>
</li>
</div>
</nav> </nav>
<div class="container" ng-view></div> <div class="container" ng-view></div>
@ -24,6 +42,7 @@
<script src="/js/angular.min.js"></script> <script src="/js/angular.min.js"></script>
<script src="/js/angular-resource.min.js"></script> <script src="/js/angular-resource.min.js"></script>
<script src="/js/angular-route.min.js"></script> <script src="/js/angular-route.min.js"></script>
<script src="/js/angular-sanitize.min.js"></script>
<script src="/js/ckhome.js"></script> <script src="/js/ckhome.js"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
angular.module("CheckHomeApp", ["ngRoute", "ngResource"]) angular.module("CheckHomeApp", ["ngRoute", "ngResource", "ngSanitize"])
.config(function($routeProvider, $locationProvider) { .config(function($routeProvider, $locationProvider) {
$routeProvider $routeProvider
.when("/items", { .when("/items", {
@ -60,18 +60,51 @@ angular.module("CheckHomeApp")
}) })
}); });
function newCheck(ItemChecks, iid, passed, cb) {
var c = new ItemChecks();
c.passed = passed;
c.comment = $("#comment").val();
c.$save({itemId: iid}, function(res) {
$("#comment").val("");
$('#commentModal').modal("hide");
cb(res);
});
}
angular.module("CheckHomeApp") angular.module("CheckHomeApp")
.controller("VersionController", function($scope, Version) { .controller("VersionController", function($scope, Version) {
$scope.v = Version.get(); $scope.v = Version.get();
}) })
.controller("RoomsController", function($scope, Room) { .controller("RoomsController", function(ItemChecks, Room, $scope, $rootScope) {
$scope.rooms = Room.query(); $scope.rooms = Room.query();
$rootScope.tagsX = [];
$rootScope.tagsReverse = false;
$rootScope.tagsShown = {};
$rootScope.stateShown = { "yes": true, "yesbut": true, "nobut": true, "no": true, "next": false , "na": true };
$scope.newRoom = function() { $scope.newRoom = function() {
var t = new Room(); var t = new Room();
t.edit = true; t.edit = true;
$scope.rooms.push(t); $scope.rooms.push(t);
} }
$scope.toggleRoom = function() {
this.room.closed = !this.room.closed;
}
$scope.showOnlyThisRoom = function() {
$scope.rooms.forEach(function(r) {
r.closed = true;
});
this.room.closed = false;
}
$scope.toggleStateS = function(state) {
$rootScope.stateShown[state] = !$rootScope.stateShown[state];
}
$scope.toggleAllStates = function() {
Object.keys($rootScope.stateShown).forEach(function(v) {
$rootScope.stateShown[v] = $rootScope.statesSelect;
})
$rootScope.statesSelect = !$rootScope.statesSelect;
}
$scope.editRoom = function() { $scope.editRoom = function() {
this.room.edit = true; this.room.edit = true;
} }
@ -86,15 +119,63 @@ angular.module("CheckHomeApp")
$scope.room.splice(rk, 1); $scope.room.splice(rk, 1);
}); });
} }
$scope.skipCheck = function() {
$rootScope.selectedItem.action = "next";
$scope.submitModal();
}
$scope.submitModal = function() {
newCheck(ItemChecks, $rootScope.selectedItem.id, $rootScope.selectedItem.action, $rootScope.selectedItem.cb)
}
}) })
.controller("ItemsRoomController", function($scope, ItemRoom) { .controller("ItemsRoomController", function($scope, $rootScope, ItemRoom) {
$scope.items = ItemRoom.query({roomId: $scope.room.id}); $scope.items = ItemRoom.query({roomId: $scope.room.id});
$scope.items.$promise.then(function(){
$scope.items.forEach(function(item) {
if (item.tags != null)
item.tags.forEach(function(v) {
if ($rootScope.tagsX.indexOf(v) == -1) {
$rootScope.tagsX.push(v);
$rootScope.tagsShown[v] = true;
}
})
})
});
$scope.filterByTag = function() {
if (this.item.tags == null)
return true;
if (!$rootScope.tagsReverse) {
var display = false;
this.item.tags.forEach(function(v) {
$rootScope.tagsX.forEach(function(t) {
if (display || ($rootScope.tagsShown[t] && t == v))
display = true;
});
});
return display;
} else {
var display = true;
this.item.tags.forEach(function(v) {
$rootScope.tagsX.forEach(function(t) {
if (!display || ($rootScope.tagsShown[t] && t == v))
display = false;
});
});
return display;
}
}
$scope.newItem = function() { $scope.newItem = function() {
var t = new ItemRoom(); var t = new ItemRoom();
t.edit = true; t.edit = true;
$scope.items.push(t); $scope.items.push(t);
} }
$scope.toggleDescription = function() {
this.item.open = !this.item.open;
}
$scope.editItem = function() { $scope.editItem = function() {
this.item.edit = true; this.item.edit = true;
} }
@ -105,19 +186,16 @@ angular.module("CheckHomeApp")
this.item.$update({roomId: this.room.id}); this.item.$update({roomId: this.room.id});
} }
$scope.delItem = function(ik) { $scope.delItem = function(ik) {
this.item.$delete().then(function() { this.item.$delete({roomId: this.room.id}).then(function() {
$scope.items.splice(ik, 1); $scope.items.splice(ik, 1);
}); });
} }
}) })
.controller("ChecksItemController", function($scope, ItemChecks) { .controller("ChecksItemController", function($scope, ItemChecks, $rootScope) {
$rootScope.selectedItem = {};
$scope.ncomment = ""; $scope.ncomment = "";
$scope.checks = ItemChecks.query({itemId: $scope.item.id}); $scope.checks = ItemChecks.query({itemId: $scope.item.id});
$scope.registerComment = function() {
$scope.newCheck($("#assocRes").val());
}
$scope.min_checks = function() { $scope.min_checks = function() {
function state2int(state) { function state2int(state) {
switch(state) { switch(state) {
@ -129,8 +207,10 @@ angular.module("CheckHomeApp")
return 2; return 2;
case "no": case "no":
return 1; return 1;
default: case "next":
return 5; return 5;
default:
return 6;
} }
} }
var min = "N/A"; var min = "N/A";
@ -140,28 +220,43 @@ angular.module("CheckHomeApp")
}); });
return min; return min;
} }
$scope.newCheck = function(passed) { $scope.filterByCheck = function() {
var c = new ItemChecks(); if ((this.checks == null || this.checks.length == 0) && $rootScope.stateShown["na"])
c.passed = passed; return true;
c.comment = $("#comment").val();
c.$save({itemId: $scope.item.id}, function(res) { var display = false;
$("#comment").val(""); this.checks.forEach(function(v) {
$scope.checks.push(res) Object.keys($rootScope.stateShown).forEach(function(k) {
if(display || (k == v["passed"] && $rootScope.stateShown[k]))
display = true;
}); });
});
return display;
} }
$scope.checkOk = function() {
$scope.newCheck("yes"); $scope.checkCommon = function(modal, res) {
} if (modal) {
$scope.checkMok = function() { var cb = function(res) { $scope.checks.push(res); }
$("#assocRes").val("yesbut"); $rootScope.selectedItem = this.item;
$rootScope.selectedItem.action = res;
$rootScope.selectedItem.cb = cb;
$('#commentModal').modal("show"); $('#commentModal').modal("show");
document.getElementById("comment").focus();
} else {
newCheck(ItemChecks, $scope.item.id, res, function(res) { $scope.checks.push(res); });
} }
$scope.checkMko = function() {
$("#assocRes").val("nobut");
$('#commentModal').modal("show");
} }
$scope.checkKo = function() { $scope.checkOk = function(modal) {
$scope.newCheck("no"); $scope.checkCommon(modal, "yes");
}
$scope.checkMok = function(modal) {
$scope.checkCommon(modal, "yesbut");
}
$scope.checkMko = function(modal) {
$scope.checkCommon(modal, "nobut");
}
$scope.checkKo = function(modal) {
$scope.checkCommon(modal, "no");
} }
$scope.delCheck = function(ck) { $scope.delCheck = function(ck) {
@ -170,9 +265,24 @@ angular.module("CheckHomeApp")
}); });
} }
}) })
.controller("TagsController", function($scope, Tag) { .controller("TagsController", function($scope, $rootScope, Tag) {
$scope.tags = Tag.query(); $scope.tags = Tag.query();
$rootScope.tagsSelect = false;
$scope.toggleAllTags = function() {
$rootScope.tagsX.forEach(function(v) {
$rootScope.tagsShown[v] = $rootScope.tagsSelect;
})
$rootScope.tagsSelect = !$rootScope.tagsSelect;
}
$scope.toggleReverseTags = function(tag) {
$rootScope.tagsReverse = !$rootScope.tagsReverse;
}
$scope.toggleTagS = function(tag) {
$rootScope.tagsShown[tag] = !$rootScope.tagsShown[tag];
$rootScope.tagsSelect = false;
}
$scope.newTag = function() { $scope.newTag = function() {
var t = new Tag(); var t = new Tag();
t.edit = true; t.edit = true;
@ -193,7 +303,7 @@ angular.module("CheckHomeApp")
}); });
} }
}) })
.controller("TagsItemController", function($scope, TagsItem) { .controller("TagsItemController", function($scope, TagsItem, $http) {
$scope.itags = TagsItem.query({itemId: $scope.item.id}); $scope.itags = TagsItem.query({itemId: $scope.item.id});
$scope.newItemTag = function() { $scope.newItemTag = function() {
@ -202,16 +312,19 @@ angular.module("CheckHomeApp")
$scope.itags.push(t); $scope.itags.push(t);
} }
$scope.editItemTag = function() { $scope.editItemTag = function() {
this.tag.val = this.tag.id;
this.tag.edit = true; this.tag.edit = true;
} }
$scope.editItemTagPass = function() {
this.tag.editpass = true;
}
$scope.saveItemTag = function() { $scope.saveItemTag = function() {
if (this.tag.id === undefined) if (this.tag.id === undefined)
this.tag.$save({itemId: $scope.item.id, tagId: this.tag.val}) this.tag.$save({itemId: $scope.item.id, tagId: this.tag.val})
else else {
this.tag.$update(); var v = this.tag;
var val = this.tag.val;
v.$delete({itemId: $scope.item.id, tagId: v.id}).then(function() {
v.$save({itemId: $scope.item.id, tagId: val});
});
}
} }
$scope.delItemTag = function(tk) { $scope.delItemTag = function(tk) {
this.tag.$delete({itemId: $scope.item.id, tagId: this.tag.id}).then(function() { this.tag.$delete({itemId: $scope.item.id, tagId: this.tag.id}).then(function() {
@ -219,7 +332,7 @@ angular.module("CheckHomeApp")
}); });
} }
}) })
.controller("UsersController", function($scope, User) { .controller("UsersController", function($scope, User, $http, $location) {
$scope.users = User.query(); $scope.users = User.query();
$scope.newUser = function() { $scope.newUser = function() {
@ -245,4 +358,10 @@ angular.module("CheckHomeApp")
$scope.users.splice(uk, 1); $scope.users.splice(uk, 1);
}); });
} }
$scope.razchecks = function() {
if (confirm("Sûr d'effacer tous les tests effectués ?"))
$http.delete("/api/checks").then(function() {
$location.url("/");
});
}
}); });

View File

@ -1,66 +1,94 @@
<div class="row">
<div class="col-md btn-group btn-group-toggle" data-toggle="buttons" ng-controller="TagsController" style="overflow: auto; width: inherit">
<button type="button" class="btn" ng-class="{'btn-secondary': !tagsSelect, 'btn-primary': tagsSelect}" ng-click="toggleAllTags()">All</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !tagsReverse, 'btn-primary': tagsReverse}" ng-click="toggleReverseTags()"><span class="glyphicon" ng-class="{'glyphicon-resize-full': tagsReverse, 'glyphicon-resize-small': !tagsReverse}" aria-hidden="true"></span></button>
<button type="button" class="btn" ng-class="{'btn-secondary': !tagsShown[tag], 'btn-primary': tagsShown[tag]}" ng-click="toggleTagS(tag)" ng-repeat="tag in tagsX">{{tag}}</button>
</div>
<div class="col-md-auto btn-group btn-group-toggle" data-toggle="buttons" style="overflow: auto; width: inherit">
<button type="button" class="btn" ng-class="{'btn-secondary': !statesSelect, 'btn-primary': statesSelect}" ng-click="toggleAllStates()">All</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['na'], 'btn-light': stateShown['na']}" ng-click="toggleStateS('na')"><span class="glyphicon glyphicon-inbox" aria-hidden="true"></span></button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['yes'], 'btn-success': stateShown['yes']}" ng-click="toggleStateS('yes')">OK</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['yesbut'], 'btn-info': stateShown['yesbut']}" ng-click="toggleStateS('yesbut')">OK</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['nobut'], 'btn-warning': stateShown['nobut']}" ng-click="toggleStateS('nobut')">KO</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['no'], 'btn-danger': stateShown['no']}" ng-click="toggleStateS('no')">KO</button>
<button type="button" class="btn" ng-class="{'btn-secondary': !stateShown['next'], 'btn-dark': stateShown['next']}" ng-click="toggleStateS('next')"><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></button>
</div>
</div>
<div ng-repeat="room in rooms" ng-controller="ItemsRoomController"> <div ng-repeat="room in rooms" ng-controller="ItemsRoomController">
<h2> <h2 ng-click="toggleRoom()" ng-dblclick="showOnlyThisRoom()" id="room-{{room.id}}">
{{room.label}} {{room.label}}
<small class="text-muted" ng-show="room.closed">(<ng-pluralize count="items.length" when="{'0': 'aucun élément', 'one': '{} élément masqué', other: '{} éléments masqués'}"></ng-pluralize>)</small>
</h2> </h2>
<div class="card" ng-repeat="item in items" ng-controller="ChecksItemController"> <div ng-repeat="item in items" ng-controller="ChecksItemController">
<div class="card-header" ng-class="{'bg-success': min_checks() == 'yes', 'bg-info': min_checks() == 'yesbut', 'bg-warning': min_checks() == 'nobut', 'bg-danger': min_checks() == 'no', 'bg-secondary': min_checks() == 'N/A'}" ng-controller="TagsItemController"> <div class="card" ng-if="!room.closed && filterByTag() && filterByCheck()">
<div class="card-header" ng-class="{'bg-success': min_checks() == 'yes', 'bg-info': min_checks() == 'yesbut', 'bg-warning': min_checks() == 'nobut', 'bg-danger': min_checks() == 'no', 'bg-secondary': min_checks() == 'N/A'}" ng-controller="TagsItemController" ng-click="toggleDescription()">
<div class="row"> <div class="row">
<div class="col-md"> <div class="col-sm-auto">
<span ng-if="min_checks() == 'yes'" class="badge badge-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></span> <span ng-if="min_checks() == 'yes'" class="badge badge-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></span>
<span ng-if="min_checks() == 'yesbut'" class="badge badge-info"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></span> <span ng-if="min_checks() == 'yesbut'" class="badge badge-info"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></span>
<span ng-if="min_checks() == 'nobut'" class="badge badge-warning"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></span> <span ng-if="min_checks() == 'nobut'" class="badge badge-warning"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></span>
<span ng-if="min_checks() == 'no'" class="badge badge-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></span> <span ng-if="min_checks() == 'no'" class="badge badge-danger"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></span>
<span ng-if="min_checks() == 'next'" class="badge badge-dark"><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></span>
<span ng-if="min_checks() != 'next' && min_checks() != 'no' && min_checks() != 'nobut' && min_checks() != 'yes' && min_checks() != 'yesbut'" class="badge badge-secondary"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true" ng-if="!item.open"></span><span class="glyphicon glyphicon-chevron-down" aria-hidden="true" ng-if="item.open"></span></span>
<strong>{{item.label}}</strong> <strong>{{item.label}}</strong>
</div> </div>
<div class="col-md text-right"> <div class="col-sm text-right">
<button type="button" class="btn btn-outline-success" ng-click="checkOk()">OK</button> <button type="button" class="btn btn-sm btn-success" ng-click="checkOk(false)" ng-dblclick="checkOk(true)">OK</button>
<button type="button" class="btn btn-outline-info" ng-click="checkMok()">OK, mais&hellip;</button> <button type="button" class="btn btn-sm btn-info" ng-click="checkMok(true)">OK mais&hellip;</button>
<button type="button" class="btn btn-outline-warning" ng-click="checkMko()">KO, mais&hellip;</button> <button type="button" class="btn btn-sm btn-warning" ng-click="checkMko(true)">KO mais&hellip;</button>
<button type="button" class="btn btn-outline-danger" ng-click="checkKo()">KO</button> <button type="button" class="btn btn-sm btn-danger" ng-click="checkKo(true)">KO</button>
</div> </div>
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body" ng-if="item.description || item.tags" ng-show="item.open">
<p class="card-text" ng-if="item.description" ng-bind="item.description"></p> <p class="card-text" ng-if="item.description" ng-bind="item.description"></p>
<p class="card-text" ng-if="item.tags">
<span class="badge" ng-class="{'badge-secondary': !tagsShown[tag], 'badge-primary': tagsShown[tag]}" ng-repeat="tag in item.tags">{{tag}}</span>
</p>
</div> </div>
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush" ng-if="item.open">
<li class="list-group-item" ng-repeat="(ck, check) in checks"> <li class="list-group-item" ng-repeat="(ck, check) in checks">
<span ng-if="check.passed == 'yes'" class="badge badge-success">OK</span> <span ng-if="check.passed == 'yes'" class="badge badge-success">OK</span>
<span ng-if="check.passed == 'yesbut'" class="badge badge-info">OK</span> <span ng-if="check.passed == 'yesbut'" class="badge badge-info">OK</span>
<span ng-if="check.passed == 'nobut'" class="badge badge-warning">KO</span> <span ng-if="check.passed == 'nobut'" class="badge badge-warning">KO</span>
<span ng-if="check.passed == 'no'" class="badge badge-danger">KO</span> <span ng-if="check.passed == 'no'" class="badge badge-danger">KO</span>
<span ng-if="check.passed == 'next'" class="badge badge-dark"><span class="glyphicon glyphicon-calendar" aria-hidden="true"></span></span>
<a ng-click="delCheck(ck)" class="float-right" ng-if="!check.id_user"> <a ng-click="delCheck(ck)" class="float-right" ng-if="!check.id_user">
<span class="glyphicon glyphicon-minus" aria-hidden="true"></span> <span class="glyphicon glyphicon-minus" aria-hidden="true"></span>
</a> </a>
{{check.date | date : 'medium'}}<span ng-show="check.comment">&nbsp;:</span>
{{check.comment}} {{check.comment}}
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div>
<div class="modal fade" id="commentModal" tabindex="-1" role="dialog"> <div class="modal fade" id="commentModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<form ng-submit="submitModal()">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="commentModalLabel">Nouveau commentaire</h5> <h5 class="modal-title" id="commentModalLabel">{{selectedItem.label}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form> <p ng-if="selectedItem.comment" ng-bind="selectedItem"></p>
<div class="form-group"> <div class="form-group">
<label for="comment">Commentaire</label> <label for="comment">Commentaire</label>
<input type="text" class="form-control" id="comment" placeholder="Commentaire..."> <input type="text" class="form-control" id="comment" placeholder="Commentaire...">
<input type="hidden" id="assocRes">
</div> </div>
</form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-primary" ng-click="registerComment()">Inscrire le commentaire</button> <button type="submit" class="btn btn-primary" ng-class="{'btn-success': selectedItem.action == 'yes', 'btn-info': selectedItem.action == 'yesbut', 'btn-warning': selectedItem.action == 'nobut', 'btn-danger': selectedItem.action == 'no', 'btn-dark': selectedItem.action == 'next'}">Continuer &hellip;</button>
</div> <button type="button" class="btn btn-dark" ng-show="selectedItem.action == 'no'" ng-click="skipCheck()">Tester plus tard</button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,10 +4,10 @@
<ul ng-controller="ItemsRoomController"> <ul ng-controller="ItemsRoomController">
<li ng-repeat="(ik, item) in items"> <li ng-repeat="(ik, item) in items">
<span ng-if="!item.edit" ng-controller="TagsItemController"> <span ng-if="!item.edit" ng-controller="TagsItemController">
<strong>{{ item.label }}</strong> <strong ng-dblclick="editItem()">{{ item.label }}</strong>
<span class="badge badge-secondary" ng-repeat="(tk, tag) in itags"> <span class="badge badge-secondary" ng-repeat="(tk, tag) in itags">
<span ng-if="!tag.edit">{{ tag.label }}</span> <span ng-if="!tag.edit" ng-dblclick="editItemTag()">{{ tag.label }}</span>
<select ng-options="tag.id as tag.label for tag in tags track by tag.id" ng-model="tag.val" ng-if="tag.edit"> <select ng-options="tag.id as tag.label for tag in tags track by tag.id" ng-change="saveItemTag()" ng-model="tag.val" ng-if="tag.edit">
</select> </select>
<a ng-click="delItemTag(tk)" ng-if="!tag.edit"> <a ng-click="delItemTag(tk)" ng-if="!tag.edit">
<span class="glyphicon glyphicon-minus" aria-hidden="true"></span> <span class="glyphicon glyphicon-minus" aria-hidden="true"></span>
@ -26,7 +26,7 @@
<button type="button" class="btn btn-sm btn-danger" ng-click="delItem(ik)" ng-if="!item.edit"> <button type="button" class="btn btn-sm btn-danger" ng-click="delItem(ik)" ng-if="!item.edit">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button><br> </button><br>
{{ item.description }} <p ng-bind-html="item.description"></p>
</span> </span>
<form ng-if="item.edit" ng-submit="saveItem()"> <form ng-if="item.edit" ng-submit="saveItem()">
<input ng-model="item.label"> <input ng-model="item.label">

View File

@ -24,3 +24,9 @@
</button> </button>
</li> </li>
</ul> </ul>
<hr>
<button type="button" class="btn btn-sm btn-dark" ng-click="razchecks()">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> RAZ test passés
</button>

View File

@ -1,17 +1,20 @@
package ckh package ckh
import () import (
"time"
)
type Check struct { type Check struct {
Id int64 `json:"id"` Id int64 `json:"id"`
IdItem int64 `json:"id_item"` IdItem int64 `json:"id_item"`
IdUser int64 `json:"id_user"` IdUser int64 `json:"id_user"`
Date time.Time `json:"date"`
Passed string `json:"passed"` Passed string `json:"passed"`
Comment string `json:"comment",omitempty` Comment string `json:"comment",omitempty`
} }
func GetChecks() (checks []Check, err error) { func GetChecks() (checks []Check, err error) {
if rows, errr := DBQuery("SELECT id_check, id_item, id_user, passed, comment FROM checks"); errr != nil { if rows, errr := DBQuery("SELECT id_check, id_item, id_user, date, passed, comment FROM checks"); errr != nil {
return nil, errr return nil, errr
} else { } else {
defer rows.Close() defer rows.Close()
@ -19,7 +22,7 @@ func GetChecks() (checks []Check, err error) {
checks = make([]Check, 0) checks = make([]Check, 0)
for rows.Next() { for rows.Next() {
var c Check var c Check
if err = rows.Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Passed, &c.Comment); err != nil { if err = rows.Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Date, &c.Passed, &c.Comment); err != nil {
return return
} }
checks = append(checks, c) checks = append(checks, c)
@ -33,7 +36,7 @@ func GetChecks() (checks []Check, err error) {
} }
func (i Item) GetChecks() (checks []Check, err error) { func (i Item) GetChecks() (checks []Check, err error) {
if rows, errr := DBQuery("SELECT id_check, id_item, id_user, passed, comment FROM checks WHERE id_item = ?", i.Id); errr != nil { if rows, errr := DBQuery("SELECT id_check, id_item, id_user, date, passed, comment FROM checks WHERE id_item = ?", i.Id); errr != nil {
return nil, errr return nil, errr
} else { } else {
defer rows.Close() defer rows.Close()
@ -41,7 +44,7 @@ func (i Item) GetChecks() (checks []Check, err error) {
checks = make([]Check, 0) checks = make([]Check, 0)
for rows.Next() { for rows.Next() {
var c Check var c Check
if err = rows.Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Passed, &c.Comment); err != nil { if err = rows.Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Date, &c.Passed, &c.Comment); err != nil {
return return
} }
checks = append(checks, c) checks = append(checks, c)
@ -55,27 +58,27 @@ func (i Item) GetChecks() (checks []Check, err error) {
} }
func GetCheck(id int64) (c Check, err error) { func GetCheck(id int64) (c Check, err error) {
err = DBQueryRow("SELECT id_check, id_item, id_user, passed, comment FROM checks WHERE id_check = ?", id).Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Passed, &c.Comment) err = DBQueryRow("SELECT id_check, id_item, id_user, date, passed, comment FROM checks WHERE id_check = ?", id).Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Date, &c.Passed, &c.Comment)
return return
} }
func (i Item) GetCheck(id int64) (c Check, err error) { func (i Item) GetCheck(id int64) (c Check, err error) {
err = DBQueryRow("SELECT id_check, id_item, id_user, passed, comment FROM checks WHERE id_check = ? AND id_item = ?", id, i.Id).Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Passed, &c.Comment) err = DBQueryRow("SELECT id_check, id_item, id_user, date, passed, comment FROM checks WHERE id_check = ? AND id_item = ?", id, i.Id).Scan(&c.Id, &c.IdItem, &c.IdUser, &c.Date, &c.Passed, &c.Comment)
return return
} }
func (i Item) NewCheck(user User, state string, comment string) (Check, error) { func (i Item) NewCheck(user User, state string, comment string) (Check, error) {
if res, err := DBExec("INSERT INTO checks (id_item, id_user, passed, comment) VALUES (?, ?, ?, ?)", i.Id, user.Id, state, comment); err != nil { if res, err := DBExec("INSERT INTO checks (id_item, id_user, date, passed, comment) VALUES (?, ?, ?, ?, ?)", i.Id, user.Id, time.Now(), state, comment); err != nil {
return Check{}, err return Check{}, err
} else if cid, err := res.LastInsertId(); err != nil { } else if cid, err := res.LastInsertId(); err != nil {
return Check{}, err return Check{}, err
} else { } else {
return Check{cid, i.Id, user.Id, state, comment}, nil return Check{cid, i.Id, user.Id, time.Now(), state, comment}, nil
} }
} }
func (c Check) Update() (int64, error) { func (c Check) Update() (int64, error) {
if res, err := DBExec("UPDATE checks SET id_item = ?, id_user = ?, passed = ?, comment = ? WHERE id_check = ?", c.IdItem, c.IdUser, c.Passed, c.Comment, c.Id); err != nil { if res, err := DBExec("UPDATE checks SET id_item = ?, id_user = ?, date = ?, passed = ?, comment = ? WHERE id_check = ?", c.IdItem, c.IdUser, c.Date, c.Passed, c.Comment, c.Id); err != nil {
return 0, err return 0, err
} else if nb, err := res.RowsAffected(); err != nil { } else if nb, err := res.RowsAffected(); err != nil {
return 0, err return 0, err

View File

@ -63,7 +63,7 @@ CREATE TABLE IF NOT EXISTS tags (id_tag INTEGER NOT NULL PRIMARY KEY AUTO_INCREM
CREATE TABLE IF NOT EXISTS items (id_item INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, label TEXT NOT NULL, description TEXT NOT NULL, id_room INTEGER); CREATE TABLE IF NOT EXISTS items (id_item INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, label TEXT NOT NULL, description TEXT NOT NULL, id_room INTEGER);
CREATE TABLE IF NOT EXISTS item_tag (id_item INTEGER NOT NULL, id_tag INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS item_tag (id_item INTEGER NOT NULL, id_tag INTEGER NOT NULL);
CREATE TABLE IF NOT EXISTS users (id_user INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255), password BINARY(64)); CREATE TABLE IF NOT EXISTS users (id_user INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255), password BINARY(64));
CREATE TABLE IF NOT EXISTS checks (id_check INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_item INTEGER NOT NULL, id_user INTEGER NOT NULL, passed ENUM('yes', 'no', 'yesbut', 'nobut') NOT NULL, comment TEXT NOT NULL); CREATE TABLE IF NOT EXISTS checks (id_check INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, id_item INTEGER NOT NULL, id_user INTEGER NOT NULL, date DATETIME DEFAULT CURRENT_TIMESTAMP, passed ENUM('yes', 'no', 'yesbut', 'nobut', 'next') NOT NULL, comment TEXT NOT NULL);
` `
for _, ln := range strings.Split(ct, "\n") { for _, ln := range strings.Split(ct, "\n") {
if len(ln) == 0 { if len(ln) == 0 {