diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 2c5275f9..b7a4f1a1 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -39,7 +39,7 @@ taiga.sessionId = taiga.generateUniqueSessionIdentifier() configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider, $compileProvider, $translateProvider) -> $routeProvider.when("/", - {templateUrl: "home/home-page.html"}) + {templateUrl: "home/home-page.html", resolve: {loader: tgLoaderProvider.add()}}) $routeProvider.when("/projects/", {templateUrl: "projects/projects-page.html", resolve: {loader: tgLoaderProvider.add()}}) @@ -320,6 +320,7 @@ modules = [ "taigaEvents", # Specific Modules + "taigaHome", "taigaNavigationBar", "taigaProjects", "taigaRelatedTasks", diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index 5aef4043..b74f30bf 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -69,9 +69,7 @@ urls = { "project-search": "/project/:project/search" "project-userstories-detail": "/project/:project/us/:ref" - "project-tasks-detail": "/project/:project/task/:ref" - "project-issues-detail": "/project/:project/issue/:ref" "project-wiki": "/project/:project/wiki" diff --git a/app/coffee/modules/resources/issues.coffee b/app/coffee/modules/resources/issues.coffee index 0cbb99de..bf7c27df 100644 --- a/app/coffee/modules/resources/issues.coffee +++ b/app/coffee/modules/resources/issues.coffee @@ -41,6 +41,9 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) -> params.ref = ref return $repo.queryOne("issues", "by_ref", params) + service.listInAllProjects = (filters) -> + return $repo.queryMany("issues", filters) + service.list = (projectId, filters, options) -> params = {project: projectId} params = _.extend({}, params, filters or {}) diff --git a/app/coffee/modules/resources/tasks.coffee b/app/coffee/modules/resources/tasks.coffee index 56a00de3..7069012c 100644 --- a/app/coffee/modules/resources/tasks.coffee +++ b/app/coffee/modules/resources/tasks.coffee @@ -41,6 +41,9 @@ resourceProvider = ($repo, $http, $urls, $storage) -> params.ref = ref return $repo.queryOne("tasks", "by_ref", params) + service.listInAllProjects = (filters) -> + return $repo.queryMany("tasks", filters) + service.list = (projectId, sprintId=null, userStoryId=null) -> params = {project: projectId} params.milestone = sprintId if sprintId diff --git a/app/coffee/modules/resources/userstories.coffee b/app/coffee/modules/resources/userstories.coffee index ac43dfff..de2f8c9b 100644 --- a/app/coffee/modules/resources/userstories.coffee +++ b/app/coffee/modules/resources/userstories.coffee @@ -38,6 +38,9 @@ resourceProvider = ($repo, $http, $urls, $storage) -> params.ref = ref return $repo.queryOne("userstories", "by_ref", params) + service.listInAllProjects = (filters) -> + return $repo.queryMany("userstories", filters) + service.listUnassigned = (projectId, filters) -> params = {"project": projectId, "milestone": "null"} params = _.extend({}, params, filters or {}) diff --git a/app/locales/locale-en.json b/app/locales/locale-en.json index 19f7268e..4dc5faf1 100644 --- a/app/locales/locale-en.json +++ b/app/locales/locale-en.json @@ -101,6 +101,9 @@ } }, "SEE_USER_PROFILE": "See {{username }} profile", + "USER_STORY": "User story", + "TASK": "Task", + "ISSUE": "Issue", "TAGS": { "PLACEHOLDER": "I'm it! Tag me...", "DELETE": "Delete tag", @@ -241,6 +244,9 @@ "REGISTER": "Register", "CREATE_ACCOUNT": "create your free account here" }, + "HOME": { + "EMPTY_WATCHING": "Follow the projects, User Stories, Tasks, Issues... that you want to know about :)" + }, "ATTACHMENT": { "SECTION_NAME": "attachments", "TITLE": "{{ fileName }} uploaded on {{ date }}", diff --git a/app/modules/home/duties/duty-directive.coffee b/app/modules/home/duties/duty-directive.coffee new file mode 100644 index 00000000..72f544a3 --- /dev/null +++ b/app/modules/home/duties/duty-directive.coffee @@ -0,0 +1,37 @@ +DutyDirective = (navurls, projectsService, $translate) -> + link = (scope, el, attrs, ctrl) -> + scope.vm = {} + scope.vm.duty = scope.duty + + scope.vm.getDutyType = () -> + if scope.vm.duty + if scope.vm.duty._name == "userstories" + return $translate.instant("COMMON.USER_STORY") + if scope.vm.duty._name == "tasks" + return $translate.instant("COMMON.TASK") + if scope.vm.duty._name == "issues" + return $translate.instant("COMMON.ISSUE") + + scope.vm.getUrl = () -> + if scope.vm.duty + ctx = { + project: projectsService.projectsById.get(String(scope.vm.duty.project)).slug + ref: scope.vm.duty.ref + } + return navurls.resolve("project-#{scope.vm.duty._name}-detail", ctx) + + scope.vm.getProjectName = () -> + if scope.vm.duty + return projectsService.projectsById.get(String(scope.vm.duty.project)).name + + directive = { + templateUrl: "home/duties/duty.html" + scope: { + "duty": "=tgDuty" + } + link: link + } + + return directive + +angular.module("taigaHome").directive("tgDuty", ["$tgNavUrls", "tgProjects", "$translate", DutyDirective]) diff --git a/app/modules/home/duties/duty.jade b/app/modules/home/duties/duty.jade new file mode 100644 index 00000000..dcb8fa5e --- /dev/null +++ b/app/modules/home/duties/duty.jade @@ -0,0 +1,12 @@ +img.avatar( + src="https://s3.amazonaws.com/uifaces/faces/twitter/rem/128.jpg" + title="{{ user.fullname }}") + +div.duty-data + div + span.duty-type {{ vm.getDutyType() }} + span.duty-status(ng-style="{'color': vm.duty.status_color}") {{ ::vm.duty.status_name }} + a.duty-title(href="{{ vm.getUrl() }}") + span.duty-id(tg-bo-ref="duty.ref") + span.duty-name {{ ::duty.subject }} +div.duty-project {{ ::vm.getProjectName()}} diff --git a/app/modules/home/home-directive.coffee b/app/modules/home/home-directive.coffee index c5218db5..a929d370 100644 --- a/app/modules/home/home-directive.coffee +++ b/app/modules/home/home-directive.coffee @@ -1,9 +1,26 @@ -HomeDirective = -> +HomeDirective = (homeService) -> + link = (scope, el, attrs, ctrl) -> + scope.vm = {} + taiga.defineImmutableProperty(scope.vm, "workInProgress", () -> homeService.workInProgress) + + scope.$watch "vm.workInProgress", (workInProgress) -> + if workInProgress.size > 0 + userStories = workInProgress.get("assignedTo").get("userStories") + tasks = workInProgress.get("assignedTo").get("tasks") + issues = workInProgress.get("assignedTo").get("issues") + scope.vm.assignedTo = userStories.concat(tasks).concat(issues) + + userStories = workInProgress.get("watching").get("userStories") + tasks = workInProgress.get("watching").get("tasks") + issues = workInProgress.get("watching").get("issues") + scope.vm.watching = userStories.concat(tasks).concat(issues) + directive = { templateUrl: "home/home.html" scope: {} + link: link } return directive -angular.module("taigaProjects").directive("tgHome", HomeDirective) +angular.module("taigaHome").directive("tgHome", ["tgHomeService", HomeDirective]) diff --git a/app/modules/home/home-page.controller.coffee b/app/modules/home/home-page.controller.coffee index eddc1b65..36eeba8f 100644 --- a/app/modules/home/home-page.controller.coffee +++ b/app/modules/home/home-page.controller.coffee @@ -12,22 +12,28 @@ class ProjectsPageController extends taiga.Controller "$tgConfig", "tgLoader", "tgProjects", + "tgHomeService", "$translate" ] constructor: (@scope, @q, @rs, @rootscope, @navUrls, @auth, @location, - @appTitle, @projectUrl, @config, tgLoader, @projects, @translate) -> + @appTitle, @projectUrl, @config, tgLoader, @projectsService, @homeService, + @translate) -> @appTitle.set(@translate.instant("PROJECT.WELCOME")) if !@auth.isAuthenticated() @location.path(@navUrls.resolve("login")) #Projects - promise = @projects.fetchProjects() + projectsPromise = @projectsService.fetchProjects() + + #In progress work + user = @auth.getUser() + workInProgressPromise = @homeService.fetchWorkInProgress(user.id) # Finally - promise.finally tgLoader.pageLoaded + @q.all([projectsPromise, workInProgressPromise]).finally tgLoader.pageLoaded angular.module("taigaHome").controller("HomePage", ProjectsPageController) diff --git a/app/modules/home/home-service.coffee b/app/modules/home/home-service.coffee new file mode 100644 index 00000000..a3b97fce --- /dev/null +++ b/app/modules/home/home-service.coffee @@ -0,0 +1,59 @@ +class HomeService extends taiga.Service + @.$inject = ["$q", "$tgResources", "$rootScope", "$projectUrl"] + + constructor: (@q, @rs, @rootScope, @projectUrl) -> + @.workInProgress = Immutable.Map() + @.inProgress = false + + fetchWorkInProgress: (userId) -> + if not @.inProgress + @.inProgress = true + params = { + status__is_closed: false + assigned_to: userId + } + assignedUserStoriesPromise = @rs.userstories.listInAllProjects(params).then (userstories) => + @.assignedToUserStories = userstories + + assignedTasksPromise = @rs.tasks.listInAllProjects(params).then (tasks) => + @.assignedToTasks = tasks + + assignedIssuesPromise = @rs.issues.listInAllProjects(params).then (issues) => + @.assignedToIssues = issues + + params = { + status__is_closed: false + watchers: userId + } + watchingUserStoriesPromise = @rs.userstories.listInAllProjects(params).then (userstories) => + @.watchingUserStories = userstories + + watchingTasksPromise = @rs.tasks.listInAllProjects(params).then (tasks) => + @.watchingTasks = tasks + + watchingIssuesPromise = @rs.issues.listInAllProjects(params).then (issues) => + @.watchingIssues = issues + + workPromise = @q.all([assignedUserStoriesPromise, assignedTasksPromise, + assignedIssuesPromise, watchingUserStoriesPromise, + watchingUserStoriesPromise, watchingIssuesPromise]) + + workPromise.then => + @.workInProgress = Immutable.fromJS({ + assignedTo: { + userStories: @.assignedToUserStories + tasks: @.assignedToTasks + issues: @.assignedToIssues + } + watching: { + userStories: @.watchingUserStories + tasks: @.watchingTasks + issues: @.watchingIssues + } + }) + + @.inProgress = false + + return workPromise + +angular.module("taigaHome").service("tgHomeService", HomeService) diff --git a/app/modules/home/home.jade b/app/modules/home/home.jade index de24aac1..ccb73a9d 100644 --- a/app/modules/home/home.jade +++ b/app/modules/home/home.jade @@ -1,68 +1,22 @@ doctype html include ../../partials/includes/components/beta -div.home-wrapper.centered(ng-controller="HomePage") +div.home-wrapper.centered div.duty-summary - // TODO Hide if ASSIGNED TO ==== false - div.title-bar.working-on-title Working on - // TODO Hide if ASSIGNED TO ==== false - section.working-on - // TODO Remove and replace for an angular repeat - - for (var x = 0; x < 2; x++) - div.duty-single - img.avatar(src="https://s3.amazonaws.com/uifaces/faces/twitter/rem/128.jpg", title="{{ user.fullname }}") - div.duty-data - div - // TODO, ADD THEIR COLOR TO THE DUTY TYPE AND STATUS - span.duty-type User Story - span.duty-status New - a.duty-title(href="") - span.duty-id #1504 - span.duty-name Documentación III: (notificaciones asíncronas + plugins front/back) - div.duty-project Taiga - div.duty-single.blocked - img.avatar(src="https://s3.amazonaws.com/uifaces/faces/twitter/rem/128.jpg", title="{{ user.fullname }}") - div.duty-data - div - // TODO, ADD THEIR COLOR TO THE DUTY TYPE AND STATUS - span.duty-type User Story - span.duty-status Blocked - a.duty-title(href="") - span.duty-id #1504 - span.duty-name Documentación III: (notificaciones asíncronas + plugins front/back) Documentación III: (notificaciones asíncronas + plugins front/back) - div.duty-project Whatever + // TODO: if not assigned to items? + // TODO: i18n + div.title-bar.working-on-title(ng-show="vm.assignedTo") Working on + section.working-on(ng-show="vm.assignedTo") + div.duty-single(tg-duty="duty", tg-repeat="duty in vm.assignedTo", ng-class="{blocked: duty.is_blocked}") div.title-bar.watching-title Watching - // TODO Show if WATCHERS ==== false - section.watching-empty.hidden + section.watching-empty(ng-show="!vm.watching.size") include ../../svg/hide.svg - p Follow the projects, User Stories, Tasks, Issues... that you want to know about :) + // TODO: i18n + p(translate="HOME.EMPTY_WATCHING") + + section.watching(ng-show="vm.watching") + div.duty-single(tg-duty="duty", tg-repeat="duty in vm.watching", ng-class="{blocked: duty.is_blocked}") - // TODO Show if WATCHERS ==== true - section.watching - - for (var x = 0; x < 20; x++) - div.duty-single - img.avatar(src="https://s3.amazonaws.com/uifaces/faces/twitter/annapickard/128.jpg", title="{{ user.fullname }}") - div.duty-data - div - // TODO, ADD THEIR COLOR TO THE DUTY TYPE AND STATUS - span.duty-type User Story - span.duty-status New - a.duty-title(href="") - span.duty-id #1504 - span.duty-name Documentación III: (notificaciones asíncronas + plugins front/back) - div.duty-project Taiga - div.duty-single - img.avatar(src="https://s3.amazonaws.com/uifaces/faces/twitter/sircookieface/128.jpg", title="{{ user.fullname }}") - div.duty-data - div - // TODO, ADD THEIR COLOR TO THE DUTY TYPE AND STATUS - span.duty-type Bug - span.duty-status Ready for test - a.duty-title(href="") - span.duty-id #28 - span.duty-name It is not possible to re-order stories in the sprint view . - div.duty-project Teletransportation hubs - a.button-gray.see-more(href="#", title="See more Watching US") See more aside.project-list(tg-home-project-list) diff --git a/app/modules/home/projects/list.jade b/app/modules/home/projects/list.jade index e76b6ed3..5fadc4d1 100644 --- a/app/modules/home/projects/list.jade +++ b/app/modules/home/projects/list.jade @@ -1,4 +1,4 @@ -ul.home-project-list(ng-show="vm.projects.length") +ul.home-project-list(ng-show="vm.projects.size") li.home-project-list-single(tg-bind-scope, tg-repeat="project in vm.projects") a(href="#", tg-nav="project:project=project.slug") h2.home-project-list-single-title @@ -7,8 +7,8 @@ ul.home-project-list(ng-show="vm.projects.length") include ../../../svg/lock.svg p {{ ::project.description | limitTo:150 }} span(ng-if="::project.description.length > 150") ... -a.see-more-projects-btn.button-gray(ng-show="vm.projects.length", href="#", tg-nav="projects", title="{{'PROJECT.NAVIGATION.SEE_MORE_PROJECTS' | translate}}", translate="PROJECT.NAVIGATION.SEE_MORE_PROJECTS") -section.projects-empty(ng-hide="vm.projects.length") +a.see-more-projects-btn.button-gray(ng-show="vm.projects.size", href="#", tg-nav="projects", title="{{'PROJECT.NAVIGATION.SEE_MORE_PROJECTS' | translate}}", translate="PROJECT.NAVIGATION.SEE_MORE_PROJECTS") +section.projects-empty(ng-hide="vm.projects.size") include ../../../svg/empty-project.svg p You don't have any projects yet a.create-project-btn.button-green( @@ -16,5 +16,3 @@ section.projects-empty(ng-hide="vm.projects.length") ng-click="vm.newProject()", title="{{'PROJECT.NAVIGATION.ACTION_CREATE_PROJECT' | translate}}", translate="PROJECT.NAVIGATION.ACTION_CREATE_PROJECT") - - diff --git a/app/modules/projects/projects-page.controller.coffee b/app/modules/projects/projects-page.controller.coffee index 1496e80a..97cafb50 100644 --- a/app/modules/projects/projects-page.controller.coffee +++ b/app/modules/projects/projects-page.controller.coffee @@ -11,12 +11,13 @@ class ProjectsPageController extends taiga.Controller "$projectUrl", "$tgConfig", "tgLoader", - "tgProjects" + "tgProjects", + "$translate" ] constructor: (@scope, @q, @rs, @rootscope, @navUrls, @auth, @location, - @appTitle, @projectUrl, @config, tgLoader, @projects) -> - @appTitle.set("Projects") + @appTitle, @projectUrl, @config, tgLoader, @projects, @translate) -> + @appTitle.set(@translate.instant("PROJECT.SECTION_PROJECTS")) if !@auth.isAuthenticated() @location.path(@navUrls.resolve("login")) diff --git a/app/modules/projects/projects.service.coffee b/app/modules/projects/projects.service.coffee index 0ed2e2c4..65edc942 100644 --- a/app/modules/projects/projects.service.coffee +++ b/app/modules/projects/projects.service.coffee @@ -1,8 +1,12 @@ +taiga = @.taiga +groupBy = @.taiga.groupBy + class ProjectsService extends taiga.Service @.$inject = ["$q", "$tgResources", "$rootScope", "$projectUrl", "tgLightboxFactory"] constructor: (@q, @rs, @rootScope, @projectUrl, @lightboxFactory) -> @.projects = Immutable.Map() + @.projectsById = Immutable.Map() @.inProgress = false @.projectsPromise = null @.fetchProjects() @@ -19,6 +23,8 @@ class ProjectsService extends taiga.Service recents: projects.slice(0, 10) }) + @.projectsById = Immutable.fromJS(groupBy(projects, (p) -> p.id)) + return @.projects @.projectsPromise.then () =>