Add initial version of filters.
parent
c3e662792d
commit
65347a7d97
|
@ -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])
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue