project timeline

stable
Juanfran 2015-05-13 15:00:01 +02:00
parent dcc3e79f20
commit 25d3cdadce
20 changed files with 239 additions and 122 deletions

View File

@ -181,8 +181,9 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
}, },
access: { access: {
requiresLogin: true requiresLogin: true
} },
controller: "Profile" controller: "Profile",
controllerAs: "vm"
} }
) )

View File

@ -1151,6 +1151,7 @@
"ISSUE_CREATED": "{{username}} has created a new Issue in {{project_name}} {{obj_name}}", "ISSUE_CREATED": "{{username}} has created a new Issue in {{project_name}} {{obj_name}}",
"TASK_CREATED": "{{username}} has created a new Task in {{project_name}} {{obj_name}}", "TASK_CREATED": "{{username}} has created a new Task in {{project_name}} {{obj_name}}",
"WIKI_CREATED": "{{username}} has created a new Wiki page in {{project_name}} {{obj_name}}", "WIKI_CREATED": "{{username}} has created a new Wiki page in {{project_name}} {{obj_name}}",
"MILESTONE_CREATED": "{{username}} has created a new Milestone in {{project_name}} {{obj_name}}",
"NEW_PROJECT": "{{username}} has a new project {{project_name}}", "NEW_PROJECT": "{{username}} has a new project {{project_name}}",
"MILESTONE_UPDATED": "{{username}} has updated the milestone {{obj_name}}", "MILESTONE_UPDATED": "{{username}} has updated the milestone {{obj_name}}",
"US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}", "US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}",

View File

@ -2,7 +2,8 @@ ProfileBarDirective = () ->
return { return {
templateUrl: "profile/profile-bar/profile-bar.html", templateUrl: "profile/profile-bar/profile-bar.html",
controller: "ProfileBar", controller: "ProfileBar",
controllerAs: "vm" controllerAs: "vm",
scope: {}
} }

View File

@ -14,6 +14,7 @@ class ProfileProjectsController
return @userService.attachUserContactsToProjects(userId, projects) return @userService.attachUserContactsToProjects(userId, projects)
.then (projects) => .then (projects) =>
@.projects = projects @.projects = projects
console.log @.projects.toJS()
angular.module("taigaProfile") angular.module("taigaProfile")
.controller("ProfileProjects", ProfileProjectsController) .controller("ProfileProjects", ProfileProjectsController)

View File

@ -11,20 +11,12 @@ ProfileTabDirective = () ->
ctrl.addTab(scope.tab) ctrl.addTab(scope.tab)
scope.$watch "tab.active", (active) ->
if active
transclude scope, (clone, scope) ->
element.append(clone)
else
element.children().each (idx, elm) ->
scope.$$childHead.$destroy()
elm.remove()
return { return {
scope: {} templateUrl: "profile/profile-tab/profile-tab.html",
require: "^tgProfileTabs" scope: {},
link: link require: "^tgProfileTabs",
transclude: true link: link,
transclude: true,
replace: true replace: true
} }

View File

@ -0,0 +1,2 @@
div(ng-if="tab.active")
ng-transclude

View File

@ -5,8 +5,8 @@ class ProfilePageController extends taiga.Controller
] ]
constructor: (@appTitle, @auth) -> constructor: (@appTitle, @auth) ->
user = @auth.getUser() @.user = @auth.userData
@appTitle.set(user.username) @appTitle.set(@.user.get('username'))
angular.module("taigaProfile").controller("Profile", ProfilePageController) angular.module("taigaProfile").controller("Profile", ProfilePageController)

View File

@ -23,7 +23,7 @@ describe "ProfileController", ->
stub = sinon.stub() stub = sinon.stub()
mocks.auth = { mocks.auth = {
getUser: sinon.stub() userData: Immutable.fromJS({username: "UserName"})
} }
provide.value "$tgAuth", mocks.auth provide.value "$tgAuth", mocks.auth
@ -44,14 +44,14 @@ describe "ProfileController", ->
inject ($controller) -> inject ($controller) ->
controller = $controller controller = $controller
it "define projects", () -> it "define user", () ->
user = {
username: "UserName"
}
mocks.auth.getUser.returns(user)
ctrl = controller "Profile", ctrl = controller "Profile",
$scope: {} $scope: {}
expect(mocks.appTitle.set.withArgs(user.username)).to.be.calledOnce expect(ctrl.user).to.be.equal(mocks.auth.userData)
it "define projects", () ->
ctrl = controller "Profile",
$scope: {}
expect(mocks.appTitle.set.withArgs("UserName")).to.be.calledOnce

View File

@ -4,7 +4,7 @@ div.profile.centered
div.main div.main
div.timeline-wrapper(tg-profile-tabs) div.timeline-wrapper(tg-profile-tabs)
div(tg-profile-tab="activity", tab-title="{{'USER.PROFILE.ACTIVITY_TAB' | translate}}", tab-icon="icon-timeline", tab-active) div(tg-profile-tab="activity", tab-title="{{'USER.PROFILE.ACTIVITY_TAB' | translate}}", tab-icon="icon-timeline", tab-active)
div(tg-user-timeline) div(tg-user-timeline, userId="vm.user.get('id')")
div(tg-profile-tab="projects", tab-title="{{'USER.PROFILE.PROJECTS_TAB' | translate}}", tab-icon="icon-project") div(tg-profile-tab="projects", tab-title="{{'USER.PROFILE.PROJECTS_TAB' | translate}}", tab-icon="icon-project")
div(tg-profile-projects) div(tg-profile-projects)

View File

@ -2,11 +2,13 @@ class ProjectController
@.$inject = [ @.$inject = [
"tgProjectsService", "tgProjectsService",
"$routeParams", "$routeParams",
"$appTitle" "$appTitle",
"$tgAuth"
] ]
constructor: (@projectsService, @routeParams, @appTitle) -> constructor: (@projectsService, @routeParams, @appTitle, @auth) ->
projectSlug = @routeParams.pslug projectSlug = @routeParams.pslug
@.user = @auth.userData
@projectsService.getProjectBySlug(projectSlug) @projectsService.getProjectBySlug(projectSlug)
.then (project) => .then (project) =>

View File

@ -1,4 +1,4 @@
describe "ProfileBar", -> describe "ProjectController", ->
$controller = null $controller = null
$q = null $q = null
provide = null provide = null
@ -19,6 +19,13 @@ describe "ProfileBar", ->
provide.value "$appTitle", mocks.appTitle provide.value "$appTitle", mocks.appTitle
_mockAuth = () ->
mocks.auth = {
userData: Immutable.fromJS({username: "UserName"})
}
provide.value "$tgAuth", mocks.auth
_mockRouteParams = () -> _mockRouteParams = () ->
provide.value "$routeParams", { provide.value "$routeParams", {
pslug: "project-slug" pslug: "project-slug"
@ -30,6 +37,7 @@ describe "ProfileBar", ->
_mockProjectsService() _mockProjectsService()
_mockRouteParams() _mockRouteParams()
_mockAppTitle() _mockAppTitle()
_mockAuth()
return null return null
@ -44,6 +52,18 @@ describe "ProfileBar", ->
_mocks() _mocks()
_inject() _inject()
it "set local user", () ->
thenStub = sinon.stub()
mocks.projectService.getProjectBySlug.withArgs("project-slug").returns({
then: thenStub
})
ctrl = $controller "Project",
$scope: {}
expect(ctrl.user).to.be.equal(mocks.auth.userData)
it "set page title", () -> it "set page title", () ->
project = Immutable.fromJS({ project = Immutable.fromJS({
name: "projectName" name: "projectName"
@ -59,8 +79,6 @@ describe "ProfileBar", ->
thenStub.callArg(0, project) thenStub.callArg(0, project)
$rootScope.$apply()
expect(mocks.appTitle.set.withArgs("projectName")).to.be.calledOnce expect(mocks.appTitle.set.withArgs("projectName")).to.be.calledOnce
@ -79,6 +97,4 @@ describe "ProfileBar", ->
thenStub.callArg(0, project) thenStub.callArg(0, project)
$rootScope.$apply()
expect(ctrl.project).to.be.equal(project) expect(ctrl.project).to.be.equal(project)

View File

@ -10,8 +10,8 @@ div.wrapper
div.tags-block(tg-colorize-tags="vm.project.get('tags')", tg-colorize-tags-type="backlog") div.tags-block(tg-colorize-tags="vm.project.get('tags')", tg-colorize-tags-type="backlog")
div.project-data div.project-data
section.timeline section.timeline(ng-if="vm.project")
div(tg-user-timeline) div(tg-user-timeline, projectId="vm.project.get('id')", userId="vm.user.get('id')")
section.involved-data section.involved-data
h2.title {{"PROJECT.SECTION.TEAM" | translate}} h2.title {{"PROJECT.SECTION.TEAM" | translate}}
ul.involved-team ul.involved-team

View File

@ -22,6 +22,17 @@ Resource = (urlsService, http) ->
url = urlsService.resolve("bulk-update-projects-order") url = urlsService.resolve("bulk-update-projects-order")
return http.post(url, bulkData) return http.post(url, bulkData)
service.getTimeline = (projectId, page) ->
params = {
page: page
}
url = urlsService.resolve("timeline-project")
url = "#{url}/#{projectId}"
return http.get(url, params).then (result) ->
return Immutable.fromJS(result.data)
return () -> return () ->
return {"projects": service} return {"projects": service}

View File

@ -45,7 +45,7 @@ Resource = (urlsService, http) ->
url = urlsService.resolve("timeline-profile") url = urlsService.resolve("timeline-profile")
url = "#{url}/#{userId}" url = "#{url}/#{userId}"
return http.get(url, params).then (result) => return http.get(url, params).then (result) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
return () -> return () ->

View File

@ -49,6 +49,12 @@ timelineType = (timeline, event) ->
key: 'TIMELINE.TASK_CREATED', key: 'TIMELINE.TASK_CREATED',
translate_params: ['username', 'project_name', 'obj_name'] translate_params: ['username', 'project_name', 'obj_name']
}, },
{ # NewMilestone
check: (timeline, event) ->
return event.obj == 'milestone' && event.type == 'create'
key: 'TIMELINE.MILESTONE_CREATED',
translate_params: ['username', 'project_name', 'obj_name']
},
{ # NewUsComment { # NewUsComment
check: (timeline, event) -> check: (timeline, event) ->
return timeline.data.comment && event.obj == 'userstory' return timeline.data.comment && event.obj == 'userstory'

View File

@ -25,23 +25,29 @@ mixOf = @.taiga.mixOf
class UserTimelineController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin) class UserTimelineController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
@.$inject = [ @.$inject = [
"$tgAuth",
"tgUserTimelineService" "tgUserTimelineService"
] ]
constructor: (@auth, @userTimelineService) -> constructor: (@userTimelineService) ->
@.timelineList = Immutable.List() @.timelineList = Immutable.List()
@.page = 1 @.page = 1
@.loadingData = false @.loadingData = false
loadTimeline: () -> loadTimeline: () ->
user = @auth.getUser()
@.loadingData = true @.loadingData = true
if @.projectId
@userTimelineService @userTimelineService
.getTimeline(user.id, @.page) .getProjectTimeline(@.projectId, @.page)
.then (newTimelineList) => .then (newTimelineList) =>
@._timelineLoaded(newTimelineList)
else
@userTimelineService
.getTimeline(@.userId, @.page)
.then (newTimelineList) =>
@._timelineLoaded(newTimelineList)
_timelineLoaded: (newTimelineList) ->
@.timelineList = @.timelineList.concat(newTimelineList) @.timelineList = @.timelineList.concat(newTimelineList)
@.page++ @.page++
@.loadingData = false @.loadingData = false

View File

@ -1,5 +1,5 @@
describe "UserTimelineController", -> describe "UserTimelineController", ->
myCtrl = scope = $q = provide = null controller = scope = $q = provide = null
mocks = {} mocks = {}
@ -7,42 +7,37 @@ describe "UserTimelineController", ->
_mockUserTimeline = () -> _mockUserTimeline = () ->
mocks.userTimelineService = { mocks.userTimelineService = {
getTimeline: sinon.stub() getTimeline: sinon.stub(),
getProjectTimeline: sinon.stub()
} }
provide.value "tgUserTimelineService", mocks.userTimelineService provide.value "tgUserTimelineService", mocks.userTimelineService
_mockTgAuth = () ->
provide.value "$tgAuth", {
getUser: () ->
return mockUser
}
_mocks = () -> _mocks = () ->
module ($provide) -> module ($provide) ->
provide = $provide provide = $provide
_mockUserTimeline() _mockUserTimeline()
_mockTgAuth()
return null return null
beforeEach -> beforeEach ->
module "taigaUserTimeline" module "taigaUserTimeline"
_mocks() _mocks()
inject ($controller, _$q_) -> inject ($controller, _$q_) ->
$q = _$q_ $q = _$q_
myCtrl = $controller "UserTimeline" controller = $controller
it "timelineList should be an array", () -> it "timelineList should be an array", () ->
myCtrl = controller "UserTimeline"
expect(myCtrl.timelineList.toJS()).is.an("array") expect(myCtrl.timelineList.toJS()).is.an("array")
it "pagination starts at 1", () -> it "pagination starts at 1", () ->
myCtrl = controller "UserTimeline"
expect(myCtrl.page).to.be.equal(1) expect(myCtrl.page).to.be.equal(1)
describe "load timeline", () -> describe "load timeline", () ->
thenStub = timelineList = null timelineList = null
beforeEach () -> beforeEach () ->
timelineList = Immutable.fromJS([ timelineList = Immutable.fromJS([
@ -52,6 +47,10 @@ describe "UserTimelineController", ->
{ fake: "fake"} { fake: "fake"}
]) ])
it "the loadingData variable must be true during the timeline load", () ->
myCtrl = controller "UserTimeline"
myCtrl.userId = mockUser.id
thenStub = sinon.stub() thenStub = sinon.stub()
mocks.userTimelineService.getTimeline = sinon.stub() mocks.userTimelineService.getTimeline = sinon.stub()
@ -60,7 +59,6 @@ describe "UserTimelineController", ->
then: thenStub then: thenStub
}) })
it "the loadingData variable must be true during the timeline load", () ->
expect(myCtrl.loadingData).to.be.false expect(myCtrl.loadingData).to.be.false
myCtrl.loadTimeline() myCtrl.loadTimeline()
@ -72,6 +70,17 @@ describe "UserTimelineController", ->
expect(myCtrl.loadingData).to.be.false expect(myCtrl.loadingData).to.be.false
it "pagiantion increase one every call to loadTimeline", () -> it "pagiantion increase one every call to loadTimeline", () ->
myCtrl = controller "UserTimeline"
myCtrl.userId = mockUser.id
thenStub = sinon.stub()
mocks.userTimelineService.getTimeline = sinon.stub()
.withArgs(mockUser.id, myCtrl.page)
.returns({
then: thenStub
})
expect(myCtrl.page).to.equal(1) expect(myCtrl.page).to.equal(1)
myCtrl.loadTimeline() myCtrl.loadTimeline()
@ -81,8 +90,39 @@ describe "UserTimelineController", ->
expect(myCtrl.page).to.equal(2) expect(myCtrl.page).to.equal(2)
it "timeline items", () -> it "timeline items", () ->
myCtrl = controller "UserTimeline"
myCtrl.userId = mockUser.id
thenStub = sinon.stub()
mocks.userTimelineService.getTimeline = sinon.stub()
.withArgs(mockUser.id, myCtrl.page)
.returns({
then: thenStub
})
myCtrl.loadTimeline() myCtrl.loadTimeline()
thenStub.callArgWith(0, timelineList) thenStub.callArgWith(0, timelineList)
expect(myCtrl.timelineList.size).to.be.eql(4) expect(myCtrl.timelineList.size).to.be.eql(4)
it "project timeline items", () ->
myCtrl = controller "UserTimeline"
myCtrl.userId = mockUser.id
myCtrl.projectId = 4
thenStub = sinon.stub()
mocks.userTimelineService.getProjectTimeline = sinon.stub()
.withArgs(4, myCtrl.page)
.returns({
then: thenStub
})
myCtrl.loadTimeline()
thenStub.callArgWith(0, timelineList)
expect(myCtrl.timelineList.size).to.be.eql(4)
expect(myCtrl.page).to.equal(2)

View File

@ -3,7 +3,11 @@ UserTimelineDirective = ->
templateUrl: "user-timeline/user-timeline/user-timeline.html", templateUrl: "user-timeline/user-timeline/user-timeline.html",
controller: "UserTimeline", controller: "UserTimeline",
controllerAs: "vm", controllerAs: "vm",
scope: {} scope: {
projectId: "=projectid",
userId: "=userid"
},
bindToController: true
} }
angular.module("taigaProfile").directive("tgUserTimeline", UserTimelineDirective) angular.module("taigaProfile").directive("tgUserTimeline", UserTimelineDirective)

View File

@ -55,4 +55,10 @@ class UserTimelineService extends taiga.Service
.then (result) => .then (result) =>
return result.filter (timeline) => @._filterValidTimelineItems(timeline) return result.filter (timeline) => @._filterValidTimelineItems(timeline)
getProjectTimeline: (projectId, page) ->
return @rs.projects.getTimeline(projectId, page)
.then (result) =>
return result.filter (timeline) => @._filterValidTimelineItems(timeline)
angular.module("taigaUserTimeline").service("tgUserTimelineService", UserTimelineService) angular.module("taigaUserTimeline").service("tgUserTimelineService", UserTimelineService)

View File

@ -12,6 +12,10 @@ describe "tgUserTimelineService", ->
getTimeline: sinon.stub() getTimeline: sinon.stub()
} }
mocks.resources.projects = {
getTimeline: sinon.stub()
}
provide.value "tgResources", mocks.resources provide.value "tgResources", mocks.resources
_mocks = () -> _mocks = () ->
@ -36,7 +40,6 @@ describe "tgUserTimelineService", ->
_setup() _setup()
_inject() _inject()
it "filter invalid timeline items", (done) ->
valid_items = [ valid_items = [
{ # valid item { # valid item
event_type: "xx.tt.create", event_type: "xx.tt.create",
@ -104,6 +107,7 @@ describe "tgUserTimelineService", ->
} }
] ]
it "filter invalid user timeline items", (done) ->
userId = 3 userId = 3
page = 2 page = 2
@ -126,3 +130,27 @@ describe "tgUserTimelineService", ->
done() done()
$rootScope.$apply() $rootScope.$apply()
it "filter invalid project timeline items", (done) ->
projectId = 3
page = 2
mocks.resources.projects.getTimeline = (_projectId_, _page_) ->
expect(_projectId_).to.be.equal(projectId)
expect(_page_).to.be.equal(page)
return $q (resolve, reject) ->
resolve(Immutable.fromJS(valid_items))
userTimelineService.getProjectTimeline(projectId, page)
.then (_items_) ->
items = _items_.toJS()
expect(items).to.have.length(3)
expect(items[0]).to.be.eql(valid_items[0])
expect(items[1]).to.be.eql(valid_items[3])
expect(items[2]).to.be.eql(valid_items[5])
done()
$rootScope.$apply()