diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 211a615f..22f12efe 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -77,6 +77,7 @@ modules = [ "taigaBacklog", "taigaTaskboard", "taigaIssues", + "taigaSearch", # Vendor modules "ngRoute", diff --git a/app/coffee/modules/search.coffee b/app/coffee/modules/search.coffee new file mode 100644 index 00000000..a51e600b --- /dev/null +++ b/app/coffee/modules/search.coffee @@ -0,0 +1,181 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# 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 . +# +# File: modules/search.coffee +### + +taiga = @.taiga + +groupBy = @.taiga.groupBy +bindOnce = @.taiga.bindOnce +mixOf = @.taiga.mixOf +debounce = @.taiga.debounce +trim = @.taiga.trim + +module = angular.module("taigaSearch", []) + +############################################################################# +## Search Controller +############################################################################# + +class SearchController extends mixOf(taiga.Controller, taiga.PageMixin) + @.$inject = ["$scope", "$tgRepo", "$tgResources", "$routeParams", "$q"] + + constructor: (@scope, @repo, @rs, @params, @q) -> + @scope.sectionName = "Search" + + promise = @.loadInitialData() + promise.then null, -> + console.log "FAIL" #TODO + + # Search input watcher + @scope.searchTerm = "" + loadSearchData = debounce(200, (t) => @.loadSearchData(t)) + + @scope.$watch "searchTerm", (term) -> + return if not term + loadSearchData(term) + + loadFilters: -> + defered = @q.defer() + defered.resolve() + return defered.promise + + loadProject: -> + return @rs.projects.get(@scope.projectId).then (project) => + @scope.project = project + @scope.issueStatusById = groupBy(project.issue_statuses, (x) -> x.id) + @scope.severityById = groupBy(project.severities, (x) -> x.id) + @scope.priorityById = groupBy(project.priorities, (x) -> x.id) + @scope.membersById = groupBy(project.memberships, (x) -> x.user) + @scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id) + # @scope.usStatusList = _.sortBy(project.us_statuses, "id") + return project + + loadSearchData: (term) -> + promise = @rs.search.do(@scope.projectId, term).then (data) => + @scope.searchResults = data + return data + + return promise + + loadInitialData: -> + promise = @repo.resolve({pslug: @params.pslug}).then (data) => + @scope.projectId = data.project + return data + + return promise.then(=> @.loadProject()) + .then(=> @.loadUsersAndRoles()) + +module.controller("SearchController", SearchController) + + +############################################################################# +## Search Directive +############################################################################# + +SearchDirective = ($log, $compile, $templatecache) -> + # linkFilters = ($scope, $el, $attrs, $ctrl) -> + linkTable = ($scope, $el, $attrs, $ctrl) -> + tabsDom = $el.find("section.search-filter") + lastSeatchResults = null + + getActiveSection = (data) -> + maxVal = 0 + + selectedSectionName = null + selectedSectionData = null + + for name, value of data + continue if name == "count" + if value.length > maxVal + maxVal = value.length + + selectedSectionData = value + selectedSectionName = name + + return {name:selectedSectionName, value: selectedSectionData} + + renderFilterTabs = (data) -> + for name, value of data + continue if name == "count" + tabsDom.find("li.#{name} .num").html(value.length) + + markSectionTabActive = (section) -> + # Mark as active the item with max amount of results + tabsDom.find("a.active").removeClass("active") + tabsDom.find("li.#{section.name} a").addClass("active") + + templates = { + issues: $templatecache.get("search-issues") + tasks: $templatecache.get("search-tasks") + userstories: $templatecache.get("search-userstories") + } + + renderTableContent = (section) -> + oldElements = $el.find(".search-result-table-body").children() + oldScope = oldElements.scope() + + if oldScope + oldScope.$destroy() + oldElements.remove() + + scope = $scope.$new() + scope[section.name] = section.value + + template = angular.element.parseHTML(trim(templates[section.name])) + element = $compile(template)(scope) + $el.find(".search-result-table-body").html(element) + + $scope.$watch "searchResults", (data) -> + lastSeatchResults = data + + if data + activeSection = getActiveSection(data) + renderFilterTabs(data) + renderTableContent(activeSection) + markSectionTabActive(activeSection) + + $el.on "click", ".search-filter li > a", (event) -> + event.preventDefault() + # Do nothing if no results found. + return if not lastSeatchResults + target = angular.element(event.currentTarget) + + sectionName = target.parent().data("name") + sectionData = lastSeatchResults[sectionName] + + section = { + name: sectionName, + value: sectionData + } + + $scope.$apply -> + renderTableContent(section) + markSectionTabActive(section) + + + link = ($scope, $el, $attrs) -> + $ctrl = $el.controller() + # linkFilters($scope, $el, $attrs, $ctrl) + linkTable($scope, $el, $attrs, $ctrl) + + return {link:link} + + +module.directive("tgSearch", ["$log", "$compile", "$templateCache", SearchDirective])