Merge pull request #572 from taigaio/Issues/2648/fix-tag-filtering

same behavior for issues filters and backlog filters
stable
Alejandro 2015-07-28 08:58:47 +02:00
commit e86caf338f
11 changed files with 192 additions and 160 deletions

View File

@ -35,11 +35,13 @@ module = angular.module("taigaBacklog")
## Issues Filters Directive ## Issues Filters Directive
############################################################################# #############################################################################
BacklogFiltersDirective = ($log, $location, $templates) -> BacklogFiltersDirective = ($q, $log, $location, $templates) ->
template = $templates.get("backlog/filters.html", true) template = $templates.get("backlog/filters.html", true)
templateSelected = $templates.get("backlog/filter-selected.html", true) templateSelected = $templates.get("backlog/filter-selected.html", true)
link = ($scope, $el, $attrs) -> link = ($scope, $el, $attrs) ->
currentFiltersType = ''
$ctrl = $el.closest(".wrapper").controller() $ctrl = $el.closest(".wrapper").controller()
selectedFilters = [] selectedFilters = []
@ -50,16 +52,18 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
$el.find("h2 a.subfilter span.title").html(title) $el.find("h2 a.subfilter span.title").html(title)
$el.find("h2 a.subfilter span.title").prop("data-type", type) $el.find("h2 a.subfilter span.title").prop("data-type", type)
currentFiltersType = getFiltersType()
showCategories = -> showCategories = ->
$el.find(".filters-cats").show() $el.find(".filters-cats").show()
$el.find(".filter-list").addClass("hidden") $el.find(".filter-list").addClass("hidden")
$el.find("h2.breadcrumb").addClass("hidden") $el.find("h2.breadcrumb").addClass("hidden")
initializeSelectedFilters = (filters) -> initializeSelectedFilters = () ->
showCategories() showCategories()
selectedFilters = [] selectedFilters = []
for name, values of filters for name, values of $scope.filters
for val in values for val in values
selectedFilters.push(val) if val.selected selectedFilters.push(val) if val.selected
@ -81,43 +85,62 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
html = template({filters:filters}) html = template({filters:filters})
$el.find(".filter-list").html(html) $el.find(".filter-list").html(html)
getFiltersType = () ->
return $el.find("h2 a.subfilter span.title").prop('data-type')
reloadUserstories = () ->
currentFiltersType = getFiltersType()
$q.all([$ctrl.loadUserstories(), $ctrl.generateFilters()]).then () ->
currentFilters = $scope.filters[currentFiltersType]
renderFilters(_.reject(currentFilters, "selected"))
toggleFilterSelection = (type, id) -> toggleFilterSelection = (type, id) ->
currentFiltersType = getFiltersType()
filters = $scope.filters[type] filters = $scope.filters[type]
filter = _.find(filters, {id: taiga.toString(id)}) filter = _.find(filters, {id: id})
filter.selected = (not filter.selected) filter.selected = (not filter.selected)
if filter.selected if filter.selected
selectedFilters.push(filter) selectedFilters.push(filter)
$scope.$apply -> $scope.$apply ->
$ctrl.selectFilter(type, id) $ctrl.selectFilter(type, id)
else else
selectedFilters = _.reject(selectedFilters, filter) selectedFilters = _.reject selectedFilters, (selected) ->
$scope.$apply -> return filter.type == selected.type && filter.id == selected.id
$ctrl.unselectFilter(type, id) $ctrl.unselectFilter(type, id)
renderSelectedFilters(selectedFilters) renderSelectedFilters(selectedFilters)
currentFiltersType = $el.find("h2 a.subfilter span.title").prop('data-type')
if type == currentFiltersType if type == currentFiltersType
renderFilters(_.reject(filters, "selected")) renderFilters(_.reject(filters, "selected"))
$ctrl.loadUserstories() reloadUserstories()
selectQFilter = debounceLeading 100, (value) -> selectQFilter = debounceLeading 100, (value) ->
return if value is undefined return if value is undefined
if value.length == 0 if value.length == 0
$ctrl.replaceFilter("q", null) $ctrl.replaceFilter("q", null)
else else
$ctrl.replaceFilter("q", value) $ctrl.replaceFilter("q", value)
$ctrl.loadUserstories()
reloadUserstories()
$scope.$watch("filtersQ", selectQFilter) $scope.$watch("filtersQ", selectQFilter)
## Angular Watchers ## Angular Watchers
$scope.$on "filters:loaded", (ctx, filters) -> $scope.$on "backlog:loaded", (ctx) ->
initializeSelectedFilters(filters) initializeSelectedFilters()
$scope.$on "filters:update", (ctx, filters) -> $scope.$on "filters:update", (ctx) ->
renderFilters(filters) $ctrl.generateFilters().then () ->
filters = $scope.filters[currentFiltersType]
if currentFiltersType
renderFilters(_.reject(filters, "selected"))
## Dom Event Handlers ## Dom Event Handlers
$el.on "click", ".filters-cats > ul > li > a", (event) -> $el.on "click", ".filters-cats > ul > li > a", (event) ->
@ -126,7 +149,7 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
tags = $scope.filters[target.data("type")] tags = $scope.filters[target.data("type")]
renderFilters(_.reject(tags, "selected")) renderFilters(_.reject(tags, "selected"))
showFilters(target.attr("title"), target.data("type")) showFilters(target.attr("title"), target.data('type'))
$el.on "click", ".filters-inner > .filters-step-cat > .breadcrumb > .back", (event) -> $el.on "click", ".filters-inner > .filters-step-cat > .breadcrumb > .back", (event) ->
event.preventDefault() event.preventDefault()
@ -153,4 +176,4 @@ BacklogFiltersDirective = ($log, $location, $templates) ->
return {link:link} return {link:link}
module.directive("tgBacklogFilters", ["$log", "$tgLocation", "$tgTemplate", BacklogFiltersDirective]) module.directive("tgBacklogFilters", ["$q", "$log", "$tgLocation", "$tgTemplate", BacklogFiltersDirective])

View File

@ -96,6 +96,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@scope.$on "usform:new:success", => @scope.$on "usform:new:success", =>
@.loadUserstories() @.loadUserstories()
@.loadProjectStats() @.loadProjectStats()
@rootscope.$broadcast("filters:update")
@analytics.trackEvent("userstory", "create", "create userstory on backlog", 1) @analytics.trackEvent("userstory", "create", "create userstory on backlog", 1)
@scope.$on "sprintform:edit:success", => @scope.$on "sprintform:edit:success", =>
@ -105,9 +107,11 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@.loadSprints() @.loadSprints()
@.loadProjectStats() @.loadProjectStats()
@.loadUserstories() @.loadUserstories()
@rootscope.$broadcast("filters:update")
@scope.$on "usform:edit:success", => @scope.$on "usform:edit:success", =>
@.loadUserstories() @.loadUserstories()
@rootscope.$broadcast("filters:update")
@scope.$on("sprint:us:move", @.moveUs) @scope.$on("sprint:us:move", @.moveUs)
@scope.$on("sprint:us:moved", @.loadSprints) @scope.$on("sprint:us:moved", @.loadSprints)
@ -145,10 +149,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return stats return stats
refreshTagsColors: ->
return @rs.projects.tagsColors(@scope.projectId).then (tags_colors) =>
@scope.project.tags_colors = tags_colors
unloadClosedSprints: -> unloadClosedSprints: ->
@scope.$apply => @scope.$apply =>
@scope.closedSprints = [] @scope.closedSprints = []
@ -182,7 +182,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
resetFilters: -> resetFilters: ->
selectedTags = _.filter(@scope.filters.tags, "selected") selectedTags = _.filter(@scope.filters.tags, "selected")
selectedStatuses = _.filter(@scope.filters.statuses, "selected") selectedStatuses = _.filter(@scope.filters.status, "selected")
@scope.filtersQ = "" @scope.filtersQ = ""
@ -195,23 +195,20 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@.unselectFilter(item.type, item.id) @.unselectFilter(item.type, item.id)
@.loadUserstories() @.loadUserstories()
@rootscope.$broadcast("filters:update")
loadUserstories: -> loadUserstories: ->
@scope.httpParams = @.getUrlFilters() @scope.httpParams = @.getUrlFilters()
@rs.userstories.storeQueryParams(@scope.projectId, @scope.httpParams) @rs.userstories.storeQueryParams(@scope.projectId, @scope.httpParams)
promise = @q.all([@.refreshTagsColors(), @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams)]) promise = @rs.userstories.listUnassigned(@scope.projectId, @scope.httpParams)
return promise.then (data) => return promise.then (userstories) =>
userstories = data[1]
# NOTE: Fix order of USs because the filter orderBy does not work propertly in the partials files # NOTE: Fix order of USs because the filter orderBy does not work propertly in the partials files
@scope.userstories = _.sortBy(userstories, "backlog_order") @scope.userstories = _.sortBy(userstories, "backlog_order")
@.setSearchDataFilters() @.setSearchDataFilters()
@.filterVisibleUserstories()
@.generateFilters()
@rootscope.$broadcast("filters:loaded", @scope.filters)
# The broadcast must be executed when the DOM has been fully reloaded. # 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 # We can't assure when this exactly happens so we need a defer
scopeDefer @scope, => scopeDefer @scope, =>
@ -247,22 +244,10 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@.fillUsersAndRoles(project.members, project.roles) @.fillUsersAndRoles(project.members, project.roles)
@.initializeSubscription() @.initializeSubscription()
return promise.then(=> @.loadBacklog()) return promise
.then(=> @.loadBacklog())
filterVisibleUserstories: -> .then(=> @.generateFilters())
@scope.visibleUserstories = [] .then(=> @scope.$emit("backlog:loaded"))
# Filter by tags
@scope.visibleUserstories = _.reject @scope.userstories, (us) =>
return _.some us.tags, (tag) =>
return @isFilterSelected("tag", tag)
# Filter by status
@scope.visibleUserstories = _.filter @scope.visibleUserstories, (us) =>
if @searchdata["statuses"] && Object.keys(@searchdata["statuses"]).length
return @isFilterSelected("statuses", taiga.toString(us.status))
return true
prepareBulkUpdateData: (uses, field="backlog_order") -> prepareBulkUpdateData: (uses, field="backlog_order") ->
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]}) return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
@ -333,13 +318,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@scope.$apply => @scope.$apply =>
# Add new us to backlog userstories list # Add new us to backlog userstories list
# @scope.userstories.splice(newUsIndex, 0, us) # @scope.userstories.splice(newUsIndex, 0, us)
# @scope.visibleUserstories.splice(newUsIndex, 0, us)
args = [newUsIndex, 0].concat(usList) args = [newUsIndex, 0].concat(usList)
Array.prototype.splice.apply(@scope.userstories, args) Array.prototype.splice.apply(@scope.userstories, args)
Array.prototype.splice.apply(@scope.visibleUserstories, args)
# Execute the prefiltering of user stories
@.filterVisibleUserstories()
# Remove the us from the sprint list. # Remove the us from the sprint list.
sprint = @scope.sprintsById[oldSprintId] sprint = @scope.sprintsById[oldSprintId]
@ -376,8 +356,8 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
# Remove moving us from backlog userstories lists. # Remove moving us from backlog userstories lists.
for us, key in usList for us, key in usList
r = @scope.visibleUserstories.indexOf(us) r = @scope.userstories.indexOf(us)
@scope.visibleUserstories.splice(r, 1) @scope.userstories.splice(r, 1)
r = @scope.userstories.indexOf(us) r = @scope.userstories.indexOf(us)
@scope.userstories.splice(r, 1) @scope.userstories.splice(r, 1)
@ -439,58 +419,43 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@searchdata[name][val] = true @searchdata[name][val] = true
getUrlFilters: -> getUrlFilters: ->
return _.pick(@location.search(), "statuses", "tags", "q") return _.pick(@location.search(), "status", "tags", "q")
generateFilters: -> generateFilters: ->
urlfilters = @.getUrlFilters() urlfilters = @.getUrlFilters()
@scope.filters = {} @scope.filters = {}
#tags loadFilters = {}
plainTags = _.flatten(_.filter(_.map(@scope.visibleUserstories, "tags"))) loadFilters.project = @scope.projectId
plainTags.sort() loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.q = urlfilters.q
loadFilters.milestone = 'null'
if plainTags.length == 0 and urlfilters["tags"] return @rs.userstories.filtersData(loadFilters).then (data) =>
plainTags.push(urlfilters["tags"]) choicesFiltersFormat = (choices, type, byIdObject) =>
_.map choices, (t) ->
t.type = type
return t
@scope.filters.tags = _.map _.countBy(plainTags), (v, k) => tagsFilterFormat = (tags) =>
obj = { return _.map tags, (t) ->
id: k, t.id = t.name
type: "tags", t.type = 'tags'
name: k, return t
color: @scope.project.tags_colors[k],
count: v # Build filters data structure
} @scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.usStatusById)
obj.selected = true if @isFilterSelected("tags", obj.id) @scope.filters.tags = tagsFilterFormat(data.tags)
return obj
selectedTags = _.filter(@scope.filters.tags, "selected") selectedTags = _.filter(@scope.filters.tags, "selected")
selectedTags = _.map(selectedTags, "name") selectedTags = _.map(selectedTags, "id")
#status selectedStatuses = _.filter(@scope.filters.status, "selected")
plainStatuses = _.map(@scope.visibleUserstories, "status")
plainStatuses = _.filter plainStatuses, (status) =>
if status
return status
if plainStatuses.length == 0 and urlfilters["statuses"]
plainStatuses.push(urlfilters["statuses"])
@scope.filters.statuses = _.map _.countBy(plainStatuses), (v, k) =>
obj = {
id: k,
type: "statuses",
name: @scope.usStatusById[k].name,
color: @scope.usStatusById[k].color,
count:v
}
obj.selected = true if @isFilterSelected("statuses", obj.id)
return obj
selectedStatuses = _.filter(@scope.filters.statuses, "selected")
selectedStatuses = _.map(selectedStatuses, "id") selectedStatuses = _.map(selectedStatuses, "id")
@.markSelectedFilters(@scope.filters, urlfilters)
#store query params #store query params
@rs.userstories.storeQueryParams(@scope.projectId, { @rs.userstories.storeQueryParams(@scope.projectId, {
"status": selectedStatuses, "status": selectedStatuses,
@ -499,13 +464,31 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
"milestone": null "milestone": null
}) })
markSelectedFilters: (filters, urlfilters) ->
# Build selected filters (from url) fast lookup data structure
searchdata = {}
for name, value of _.omit(urlfilters, "page", "orderBy")
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
for key, value of filters
for obj in value
obj.selected = if isSelected(obj.type, obj.id) then true else undefined
## Template actions ## Template actions
updateUserStoryStatus: () -> updateUserStoryStatus: () ->
@.setSearchDataFilters() @.setSearchDataFilters()
@.filterVisibleUserstories() @.generateFilters().then () ->
@.generateFilters() @rootscope.$broadcast("filters:update")
@rootscope.$broadcast("filters:update", @scope.filters['statuses'])
@.loadProjectStats() @.loadProjectStats()
editUserStory: (us) -> editUserStory: (us) ->
@ -519,7 +502,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@confirm.askOnDelete(title, message).then (finish) => @confirm.askOnDelete(title, message).then (finish) =>
# We modify the userstories in scope so the user doesn't see the removed US for a while # We modify the userstories in scope so the user doesn't see the removed US for a while
@scope.userstories = _.without(@scope.userstories, us) @scope.userstories = _.without(@scope.userstories, us)
@filterVisibleUserstories()
promise = @.repo.remove(us) promise = @.repo.remove(us)
promise.then => promise.then =>
finish() finish()
@ -560,9 +542,9 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
total_points = stats.total_points total_points = stats.total_points
current_sum = stats.assigned_points current_sum = stats.assigned_points
return if not $scope.visibleUserstories return if not $scope.userstories
for us, i in $scope.visibleUserstories for us, i in $scope.userstories
current_sum += us.total_points current_sum += us.total_points
if current_sum > total_points if current_sum > total_points
@ -603,7 +585,6 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
# Update the total of points # Update the total of points
$scope.sprints[0].total_points += totalExtraPoints $scope.sprints[0].total_points += totalExtraPoints
$ctrl.filterVisibleUserstories()
$repo.saveAll(selectedUss).then -> $repo.saveAll(selectedUss).then ->
$ctrl.loadSprints() $ctrl.loadSprints()
$ctrl.loadProjectStats() $ctrl.loadProjectStats()
@ -725,7 +706,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
$el.find(".backlog-table-body").disableSelection() $el.find(".backlog-table-body").disableSelection()
filters = $ctrl.getUrlFilters() filters = $ctrl.getUrlFilters()
if filters.statuses || if filters.status ||
filters.tags || filters.tags ||
filters.q filters.q
showHideFilter($scope, $el, $ctrl) showHideFilter($scope, $el, $ctrl)

View File

@ -83,8 +83,6 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.$on "issueform:new:success", => @scope.$on "issueform:new:success", =>
@analytics.trackEvent("issue", "create", "create issue on issues list", 1) @analytics.trackEvent("issue", "create", "create issue on issues list", 1)
@.loadIssues() @.loadIssues()
@.loadFilters()
initializeSubscription: -> initializeSubscription: ->
routingKey = "changes.project.#{@scope.projectId}.issues" routingKey = "changes.project.#{@scope.projectId}.issues"
@ -115,9 +113,10 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
return project return project
getUrlFilters: -> getUrlFilters: ->
filters = _.pick(@location.search(), "page", "tags", "statuses", "types", filters = _.pick(@location.search(), "page", "tags", "status", "types",
"q", "severities", "priorities", "q", "severities", "priorities",
"assignedTo", "createdBy", "orderBy") "assignedTo", "createdBy", "orderBy")
filters.page = 1 if not filters.page filters.page = 1 if not filters.page
return filters return filters
@ -180,20 +179,30 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.filters.myFilters = myFilters @scope.filters.myFilters = myFilters
return myFilters return myFilters
loadFilters = {}
loadFilters.project = @scope.projectId
loadFilters.tags = urlfilters.tags
loadFilters.status = urlfilters.status
loadFilters.q = urlfilters.q
loadFilters.types = urlfilters.types
loadFilters.severities = urlfilters.severities
loadFilters.priorities = urlfilters.priorities
loadFilters.assigned_to = urlfilters.assignedTo
loadFilters.owner = urlfilters.createdBy
# Load default filters data # Load default filters data
promise = promise.then => promise = promise.then =>
return @rs.issues.filtersData(@scope.projectId) return @rs.issues.filtersData(loadFilters)
# Format filters and set them on scope # Format filters and set them on scope
return promise.then (data) => return promise.then (data) =>
usersFiltersFormat = (users, type, unknownOption) => usersFiltersFormat = (users, type, unknownOption) =>
reformatedUsers = _.map users, (t) => reformatedUsers = _.map users, (t) =>
return { t.type = type
id: t[0], t.name = if t.full_name then t.full_name else unknownOption
count: t[1],
type: type return t
name: if t[0] then @scope.usersById[t[0]].full_name_display else unknownOption
}
unknownItem = _.remove(reformatedUsers, (u) -> not u.id) unknownItem = _.remove(reformatedUsers, (u) -> not u.id)
reformatedUsers = _.sortBy(reformatedUsers, (u) -> u.name.toUpperCase()) reformatedUsers = _.sortBy(reformatedUsers, (u) -> u.name.toUpperCase())
if unknownItem.length > 0 if unknownItem.length > 0
@ -202,34 +211,27 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
choicesFiltersFormat = (choices, type, byIdObject) => choicesFiltersFormat = (choices, type, byIdObject) =>
_.map choices, (t) -> _.map choices, (t) ->
return { t.type = type
id: t[0], return t
name: byIdObject[t[0]].name,
color: byIdObject[t[0]].color,
count: t[1],
type: type}
tagsFilterFormat = (tags) => tagsFilterFormat = (tags) =>
return _.map tags, (t) => return _.map tags, (t) ->
return { t.id = t.name
id: t[0], t.type = 'tags'
name: t[0], return t
color: @scope.project.tags_colors[t[0]],
count: t[1],
type: "tags"
}
# Build filters data structure # Build filters data structure
@scope.filters.statuses = choicesFiltersFormat(data.statuses, "statuses", @scope.issueStatusById) @scope.filters.status = choicesFiltersFormat(data.statuses, "status", @scope.issueStatusById)
@scope.filters.severities = choicesFiltersFormat(data.severities, "severities", @scope.severityById) @scope.filters.severities = choicesFiltersFormat(data.severities, "severities", @scope.severityById)
@scope.filters.priorities = choicesFiltersFormat(data.priorities, "priorities", @scope.priorityById) @scope.filters.priorities = choicesFiltersFormat(data.priorities, "priorities", @scope.priorityById)
@scope.filters.assignedTo = usersFiltersFormat(data.assigned_to, "assignedTo", "Unassigned") @scope.filters.assignedTo = usersFiltersFormat(data.assigned_to, "assignedTo", "Unassigned")
@scope.filters.createdBy = usersFiltersFormat(data.created_by, "createdBy", "Unknown") @scope.filters.createdBy = usersFiltersFormat(data.owners, "createdBy", "Unknown")
@scope.filters.types = choicesFiltersFormat(data.types, "types", @scope.issueTypeById) @scope.filters.types = choicesFiltersFormat(data.types, "types", @scope.issueTypeById)
@scope.filters.tags = tagsFilterFormat(data.tags) @scope.filters.tags = tagsFilterFormat(data.tags)
@.removeNotExistingFiltersFromUrl() @.removeNotExistingFiltersFromUrl()
@.markSelectedFilters(@scope.filters, urlfilters) @.markSelectedFilters(@scope.filters, urlfilters)
@rootscope.$broadcast("filters:loaded", @scope.filters) @rootscope.$broadcast("filters:loaded", @scope.filters)
# We need to guarantee that the last petition done here is the finally used # We need to guarantee that the last petition done here is the finally used
@ -257,7 +259,7 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
name = "assigned_to" name = "assigned_to"
else if name == "createdBy" else if name == "createdBy"
name = "owner" name = "owner"
else if name == "statuses" else if name == "status"
name = "status" name = "status"
else if name == "types" else if name == "types"
name = "type" name = "type"
@ -272,14 +274,19 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.page = data.current @scope.page = data.current
@scope.count = data.count @scope.count = data.count
@scope.paginatedBy = data.paginatedBy @scope.paginatedBy = data.paginatedBy
return data return data
return promise
loadInitialData: -> loadInitialData: ->
promise = @.loadProject() promise = @.loadProject()
return promise.then (project) => return promise.then (project) =>
@.fillUsersAndRoles(project.members, project.roles) @.fillUsersAndRoles(project.users, project.roles)
@.initializeSubscription() @.initializeSubscription()
return @q.all([@.loadFilters(), @.loadIssues()]) @.loadFilters()
return @.loadIssues()
saveCurrentFiltersTo: (newFilter) -> saveCurrentFiltersTo: (newFilter) ->
deferred = @q.defer() deferred = @q.defer()
@ -439,12 +446,13 @@ module.directive("tgIssues", ["$log", "$tgLocation", "$tgTemplate", "$compile",
## Issues Filters Directive ## Issues Filters Directive
############################################################################# #############################################################################
IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) -> IssuesFiltersDirective = ($q, $log, $location, $rs, $confirm, $loading, $template, $translate, $compile, $auth) ->
template = $template.get("issue/issues-filters.html", true) template = $template.get("issue/issues-filters.html", true)
templateSelected = $template.get("issue/issues-filters-selected.html", true) templateSelected = $template.get("issue/issues-filters-selected.html", true)
link = ($scope, $el, $attrs) -> link = ($scope, $el, $attrs) ->
$ctrl = $el.closest(".wrapper").controller() $ctrl = $el.closest(".wrapper").controller()
selectedFilters = [] selectedFilters = []
showFilters = (title, type) -> showFilters = (title, type) ->
@ -490,6 +498,16 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
html = $compile(html)($scope) html = $compile(html)($scope)
$el.find(".filter-list").html(html) $el.find(".filter-list").html(html)
getFiltersType = () ->
return $el.find("h2 a.subfilter span.title").prop('data-type')
reloadIssues = () ->
currentFiltersType = getFiltersType()
$q.all([$ctrl.loadIssues(), $ctrl.loadFilters()]).then () ->
filters = $scope.filters[currentFiltersType]
renderFilters(_.reject(filters, "selected"))
toggleFilterSelection = (type, id) -> toggleFilterSelection = (type, id) ->
if type == "myFilters" if type == "myFilters"
$rs.issues.getMyFilters($scope.projectId).then (data) -> $rs.issues.getMyFilters($scope.projectId).then (data) ->
@ -506,7 +524,6 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
filters = $scope.filters[type] filters = $scope.filters[type]
filterId = if type == 'tags' then taiga.toString(id) else id filterId = if type == 'tags' then taiga.toString(id) else id
filter = _.find(filters, {id: filterId}) filter = _.find(filters, {id: filterId})
filter.selected = (not filter.selected) filter.selected = (not filter.selected)
# Convert id to null as string for properly # Convert id to null as string for properly
@ -515,22 +532,23 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
if filter.selected if filter.selected
selectedFilters.push(filter) selectedFilters.push(filter)
$scope.$apply ->
$ctrl.selectFilter(type, id) $ctrl.selectFilter(type, id)
$ctrl.selectFilter("page", 1) $ctrl.selectFilter("page", 1)
$ctrl.storeFilters() $ctrl.storeFilters()
$ctrl.loadIssues()
else else
selectedFilters = _.reject(selectedFilters, filter) selectedFilters = _.reject selectedFilters, (f) ->
$scope.$apply -> return f.id == filter.id && f.type == filter.type
$ctrl.unselectFilter(type, id) $ctrl.unselectFilter(type, id)
$ctrl.selectFilter("page", 1) $ctrl.selectFilter("page", 1)
$ctrl.storeFilters() $ctrl.storeFilters()
$ctrl.loadIssues()
reloadIssues()
renderSelectedFilters(selectedFilters) renderSelectedFilters(selectedFilters)
currentFiltersType = $el.find("h2 a.subfilter span.title").prop('data-type') currentFiltersType = getFiltersType()
if type == currentFiltersType if type == currentFiltersType
renderFilters(_.reject(filters, "selected")) renderFilters(_.reject(filters, "selected"))
@ -539,7 +557,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
initializeSelectedFilters(filters) initializeSelectedFilters(filters)
$scope.$on "filters:issueupdate", (ctx, filters) -> $scope.$on "filters:issueupdate", (ctx, filters) ->
html = template({filters:filters.statuses}) html = template({filters:filters.status})
html = $compile(html)($scope) html = $compile(html)($scope)
$el.find(".filter-list").html(html) $el.find(".filter-list").html(html)
@ -554,7 +572,8 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
else else
$ctrl.replaceFilter("q", value) $ctrl.replaceFilter("q", value)
$ctrl.storeFilters() $ctrl.storeFilters()
$ctrl.loadIssues()
reloadIssues()
$scope.$watch("filtersQ", selectQFilter) $scope.$watch("filtersQ", selectQFilter)
@ -661,7 +680,7 @@ IssuesFiltersDirective = ($log, $location, $rs, $confirm, $loading, $template, $
return {link:link} return {link:link}
module.directive("tgIssuesFilters", ["$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading", module.directive("tgIssuesFilters", ["$q", "$log", "$tgLocation", "$tgResources", "$tgConfirm", "$tgLoading",
"$tgTemplate", "$translate", "$compile", "$tgAuth", IssuesFiltersDirective]) "$tgTemplate", "$translate", "$compile", "$tgAuth", IssuesFiltersDirective])
@ -708,7 +727,7 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
event.stopPropagation() event.stopPropagation()
target = angular.element(event.currentTarget) target = angular.element(event.currentTarget)
for filter in $scope.filters.statuses for filter in $scope.filters.status
if filter.id == issue.status if filter.id == issue.status
filter.count-- filter.count--
@ -720,13 +739,13 @@ IssueStatusInlineEditionDirective = ($repo, $template, $rootscope) ->
$repo.save(issue).then -> $repo.save(issue).then ->
$ctrl.loadIssues() $ctrl.loadIssues()
for filter in $scope.filters.statuses for filter in $scope.filters.status
if filter.id == issue.status if filter.id == issue.status
filter.count++ filter.count++
$rootscope.$broadcast("filters:issueupdate", $scope.filters) $rootscope.$broadcast("filters:issueupdate", $scope.filters)
for filter in $scope.filters.statuses for filter in $scope.filters.status
if filter.id == issue.status if filter.id == issue.status
filter.count++ filter.count++
$rootscope.$broadcast("filters:issueupdate", $scope.filters) $rootscope.$broadcast("filters:issueupdate", $scope.filters)

View File

@ -82,6 +82,7 @@ urls = {
"bulk-update-us-backlog-order": "/userstories/bulk_update_backlog_order" "bulk-update-us-backlog-order": "/userstories/bulk_update_backlog_order"
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order" "bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order" "bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
"userstories-filters": "/userstories/filters_data"
# Tasks # Tasks
"tasks": "/tasks" "tasks": "/tasks"
@ -91,6 +92,7 @@ urls = {
# Issues # Issues
"issues": "/issues" "issues": "/issues"
"bulk-create-issues": "/issues/bulk_create" "bulk-create-issues": "/issues/bulk_create"
"issues-filters": "/issues/filters_data"
# Wiki pages # Wiki pages
"wiki": "/wiki" "wiki": "/wiki"

View File

@ -58,8 +58,8 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
service.stats = (projectId) -> service.stats = (projectId) ->
return $repo.queryOneRaw("projects", "#{projectId}/issues_stats") return $repo.queryOneRaw("projects", "#{projectId}/issues_stats")
service.filtersData = (projectId) -> service.filtersData = (params) ->
return $repo.queryOneRaw("projects", "#{projectId}/issue_filters_data") return $repo.queryOneRaw("issues-filters", null, params)
service.listValues = (projectId, type) -> service.listValues = (projectId, type) ->
params = {"project": projectId} params = {"project": projectId}

View File

@ -41,6 +41,9 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
service.listInAllProjects = (filters) -> service.listInAllProjects = (filters) ->
return $repo.queryMany("userstories", filters) return $repo.queryMany("userstories", filters)
service.filtersData = (params) ->
return $repo.queryOneRaw("userstories-filters", null, params)
service.listUnassigned = (projectId, filters) -> service.listUnassigned = (projectId, filters) ->
params = {"project": projectId, "milestone": "null"} params = {"project": projectId, "milestone": "null"}
params = _.extend({}, params, filters or {}) params = _.extend({}, params, filters or {})

View File

@ -34,10 +34,10 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
span.text(translate="BACKLOG.TAGS.SHOW") span.text(translate="BACKLOG.TAGS.SHOW")
include ../includes/components/addnewus include ../includes/components/addnewus
section.backlog-table(ng-class="{'hidden': !visibleUserstories.length}") section.backlog-table(ng-class="{'hidden': !userstories.length}")
include ../includes/modules/backlog-table include ../includes/modules/backlog-table
div.empty.empty-backlog(ng-class="{'hidden': visibleUserstories.length}", tg-backlog-empty-sortable) div.empty.empty-backlog(ng-class="{'hidden': userstories.length}", tg-backlog-empty-sortable)
span.icon.icon-backlog span.icon.icon-backlog
span.title(translate="BACKLOG.EMPTY") span.title(translate="BACKLOG.EMPTY")
a(href="", title="{{'BACKLOG.CREATE_NEW_US' | translate}}", a(href="", title="{{'BACKLOG.CREATE_NEW_US' | translate}}",

View File

@ -3,11 +3,15 @@
a.single-filter.active(data-type!="<%- f.type %>", data-id!="<%- f.id %>") a.single-filter.active(data-type!="<%- f.type %>", data-id!="<%- f.id %>")
span.name(style!="<%- f.style %>") span.name(style!="<%- f.style %>")
| <%- f.name %> | <%- f.name %>
<% if (f.count){ %>
span.number <%- f.count %> span.number <%- f.count %>
<% } %>
<% } else { %> <% } else { %>
a.single-filter(data-type!="<%- f.type %>", data-id!="<%- f.id %>") a.single-filter(data-type!="<%- f.type %>", data-id!="<%- f.id %>")
span.name(style!="<%- f.style %>") span.name(style!="<%- f.style %>")
| <%- f.name %> | <%- f.name %>
<% if (f.count){ %>
span.number <%- f.count %> span.number <%- f.count %>
<% } %>
<% } %> <% } %>
<% }) %> <% }) %>

View File

@ -1,4 +1,4 @@
div.row.us-item-row(ng-repeat="us in visibleUserstories track by us.id", tg-bind-scope, ng-class="{blocked: us.is_blocked}", tg-class-permission="{'readonly': '!modify_us'}") div.row.us-item-row(ng-repeat="us in userstories track by us.id", tg-bind-scope, ng-class="{blocked: us.is_blocked}", tg-class-permission="{'readonly': '!modify_us'}")
div.user-stories div.user-stories
div.tags-block(tg-colorize-tags="us.tags", tg-colorize-tags-type="backlog") div.tags-block(tg-colorize-tags="us.tags", tg-colorize-tags-type="backlog")
div.user-story-name div.user-story-name

View File

@ -18,7 +18,7 @@ section.filters
div.filters-cats div.filters-cats
ul ul
li li
a(href="", title="{{'BACKLOG.FILTERS.FILTER_CATEGORY_STATUS' | translate}}", data-type="statuses") a(href="", title="{{'BACKLOG.FILTERS.FILTER_CATEGORY_STATUS' | translate}}", data-type="status")
span.title(translate="BACKLOG.FILTERS.FILTER_CATEGORY_STATUS") span.title(translate="BACKLOG.FILTERS.FILTER_CATEGORY_STATUS")
span.icon.icon-arrow-right span.icon.icon-arrow-right
li li

View File

@ -22,7 +22,7 @@ section.filters
span.title(translate="ISSUES.FILTERS.CATEGORIES.TYPE") span.title(translate="ISSUES.FILTERS.CATEGORIES.TYPE")
span.icon.icon-arrow-right span.icon.icon-arrow-right
li li
a(href="", title="{{ 'ISSUES.FILTERS.CATEGORIES.STATUS' | translate}}", data-type="statuses") a(href="", title="{{ 'ISSUES.FILTERS.CATEGORIES.STATUS' | translate}}", data-type="status")
span.title(translate="ISSUES.FILTERS.CATEGORIES.STATUS") span.title(translate="ISSUES.FILTERS.CATEGORIES.STATUS")
span.icon.icon-arrow-right span.icon.icon-arrow-right
li li