Add initial version of filters.

stable
Andrey Antukh 2014-07-07 10:10:24 +02:00
parent c3e662792d
commit 65347a7d97
4 changed files with 239 additions and 22 deletions

View File

@ -0,0 +1,159 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: modules/backlog/main.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
toggleText = @.taiga.toggleText
scopeDefer = @.taiga.scopeDefer
bindOnce = @.taiga.bindOnce
groupBy = @.taiga.groupBy
debounce = @.taiga.debounce
module = angular.module("taigaBacklog")
#############################################################################
## Issues Filters Directive
#############################################################################
BacklogFiltersDirective = ($log, $location) ->
template = _.template("""
<% _.each(filters, function(f) { %>
<% if (f.selected) { %>
<a class="single-filter active"
data-type="<%= f.type %>"
data-id="<%= f.id %>">
<span class="name"><%- f.name %></span>
<span class="number"><%- f.count %></span>
</a>
<% } else { %>
<a class="single-filter"
data-type="<%= f.type %>"
data-id="<%= f.id %>">
<span class="name"><%- f.name %></span>
<span class="number"><%- f.count %></span>
</a>
<% } %>
<% }) %>
""")
templateSelected = _.template("""
<% _.each(filters, function(f) { %>
<a class="single-filter selected"
data-type="<%= f.type %>"
data-id="<%= f.id %>">
<span class="name"><%- f.name %></span>
<span class="icon icon-delete"></span>
</a>
<% }) %>
""")
selectedFilters = []
link = ($scope, $el, $attrs) ->
$ctrl = $el.closest(".wrapper").controller()
showFilters = (title) ->
$el.find(".filters-cats").hide()
$el.find(".filter-list").show()
$el.find("h1 a.subfilter").removeClass("hidden")
$el.find("h1 a.subfilter span.title").html(title)
showCategories = ->
$el.find(".filters-cats").show()
$el.find(".filter-list").hide()
$el.find("h1 a.subfilter").addClass("hidden")
initializeSelectedFilters = (filters) ->
for name, values of filters
for val in values
selectedFilters.push(val) if val.selected
renderSelectedFilters()
renderSelectedFilters = ->
html = templateSelected({filters:selectedFilters})
$el.find(".filters-applied").html(html)
renderFilters = (filters) ->
html = template({filters:filters})
$el.find(".filter-list").html(html)
toggleFilterSelection = (type, id) ->
filters = $scope.filters[type]
filter = _.find(filters, {id: taiga.toString(id)})
filter.selected = (not filter.selected)
if filter.selected
selectedFilters.push(filter)
$scope.$apply ->
$ctrl.selectFilter(type, id)
$ctrl.filterVisibleUserstories()
else
selectedFilters = _.reject(selectedFilters, filter)
$scope.$apply ->
$ctrl.unselectFilter(type, id)
$ctrl.filterVisibleUserstories()
renderSelectedFilters(selectedFilters)
renderFilters(_.reject(filters, "selected"))
# Angular Watchers
$scope.$on "filters:loaded", (ctx, filters) ->
initializeSelectedFilters(filters)
# Dom Event Handlers
$el.on "click", ".filters-cats > ul > li > a", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
tags = $scope.filters[target.data("type")]
renderFilters(_.reject(tags, "selected"))
showFilters(target.attr("title"))
$el.on "click", ".filters-inner > h1 > a.title", (event) ->
event.preventDefault()
showCategories($el)
$el.on "click", ".filters-applied a", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
id = target.data("id")
type = target.data("type")
toggleFilterSelection(type, id)
$el.on "click", ".filter-list .single-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
if target.hasClass("active")
target.removeClass("active")
# target.css("background-color")
else
target.addClass("active")
id = target.data("id")
type = target.data("type")
toggleFilterSelection(type, id)
return {link:link}
module.directive("tgBacklogFilters", ["$log", "$tgLocation", BacklogFiltersDirective])

View File

@ -33,7 +33,7 @@ module = angular.module("taigaBacklog")
## Backlog Controller
#############################################################################
class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin)
class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
@.$inject = [
"$scope",
"$rootScope",
@ -41,10 +41,11 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin)
"$tgConfirm",
"$tgResources",
"$routeParams",
"$q"
"$q",
"$tgLocation"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q) ->
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location) ->
_.bindAll(@)
@scope.sectionName = "Backlog"
@ -74,9 +75,11 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin)
loadUserstories: ->
return @rs.userstories.listUnassigned(@scope.projectId).then (userstories) =>
@scope.userstories = userstories
@scope.filters = @.generateFilters()
@.generateFilters()
@.filterVisibleUserstories()
@rootscope.$broadcast("filters:loaded", @scope.filters)
# The broadcast must be executed when the DOM has been fully reloaded.
# We can't assure when this exactly happens so we need a defer
scopeDefer @scope, =>
@ -111,25 +114,63 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin)
.then(=> @.loadBacklog())
filterVisibleUserstories: ->
@scope.visibleUserstories = []
# Filter by tags
selectedTags = _.filter(@scope.filters.tags, "selected")
selectedTags = _.map(selectedTags, "name")
@scope.visibleUserstories = []
if selectedTags.length == 0
@scope.visibleUserstories = _.clone(@scope.userstories, false)
else
@scope.visibleUserstories = _.reject @scope.userstories, (us) =>
if _.intersection(selectedTags, us.tags).length == 0
return true
else
return false
return false
# Filter by status
selectedStatuses = _.filter(@scope.filters.statuses, "selected")
selectedStatuses = _.map(selectedStatuses, "id")
if selectedStatuses.length > 0
@scope.visibleUserstories = _.reject @scope.visibleUserstories, (us) =>
res = _.find(selectedStatuses, (x) -> x == taiga.toString(us.status))
return not res
getUrlFilters: ->
return _.pick(@location.search(), "statuses", "tags")
generateFilters: ->
filters = {}
searchdata = {}
for name, value of @.getUrlFilters()
if not searchdata[name]?
searchdata[name] = {}
for val in value.split(",")
searchdata[name][val] = true
isSelected = (type, id) ->
if searchdata[type]? and searchdata[type][id]
return true
return false
@scope.filters = {}
plainTags = _.flatten(_.map(@scope.userstories, "tags"))
filters.tags = _.map(_.countBy(plainTags), (v, k) -> {name: k, count:v})
return filters
@scope.filters.tags = _.map _.countBy(plainTags), (v, k) ->
obj = {id:k, type:"tags", name: k, count:v}
obj.selected = true if isSelected("tags", obj.id)
return obj
plainStatuses = _.map(@scope.userstories, "status")
@scope.filters.statuses = _.map _.countBy(plainStatuses), (v, k) =>
obj = {id:k, type:"statuses", name: @scope.usStatusById[k].name, count:v}
obj.selected = true if isSelected("statuses", obj.id)
console.log "statuses", obj
return obj
console.log @scope.filters.statuses
return @scope.filters
## Template actions
@ -344,15 +385,6 @@ BacklogDirective = ($repo, $rootscope) ->
toggleText(target.find(".text"), ["Hide Filters", "Show Filters"]) # TODO: i18n
$rootscope.$broadcast("resize")
$el.on "click", "section.filters a.single-filter", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
targetScope = target.scope()
$scope.$apply ->
targetScope.tag.selected = not (targetScope.tag.selected or false)
$ctrl.filterVisibleUserstories()
link = ($scope, $el, $attrs, $rootscope) ->
$ctrl = $el.controller()

View File

@ -6,8 +6,8 @@ block head
block content
div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
ng-init="section='backlog'")
sidebar.menu-secondary.extrabar.filters-bar
include views/modules/filters
sidebar.menu-secondary.extrabar.filters-bar(tg-backlog-filters)
include views/modules/backlog-filters
section.main.backlog
include views/components/mainTitle
include views/components/summary

View File

@ -0,0 +1,26 @@
section.filters
div.filters-inner
h1
a.title(href="", title="back to categories") filters
a.hidden.subfilter(href="", title="cat-name")
span.icon.icon-arrow-right
span.title status
form
fieldset
input(type="text", placeholder="Search by subject...", ng-model="filtersSubject")
a.icon.icon-search(href="", title="search")
div.filters-step-cat
div.filters-applied
div.filters-cats
ul
li
a(href="", title="Status", data-type="statuses")
span.title Status
span.icon.icon-arrow-right
li
a(href="", title="Tags", data-type="tags")
span.title Tags
span.icon.icon-arrow-right
div.filter-list.hidden