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: {
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}}",
"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}}",
"MILESTONE_CREATED": "{{username}} has created a new Milestone in {{project_name}} {{obj_name}}",
"NEW_PROJECT": "{{username}} has a new project {{project_name}}",
"MILESTONE_UPDATED": "{{username}} has updated the milestone {{obj_name}}",
"US_UPDATED": "{{username}} has updated the attribute \"{{field_name}}\" of the US {{obj_name}}",

View File

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

View File

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

View File

@ -11,20 +11,12 @@ ProfileTabDirective = () ->
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 {
scope: {}
require: "^tgProfileTabs"
link: link
transclude: true
templateUrl: "profile/profile-tab/profile-tab.html",
scope: {},
require: "^tgProfileTabs",
link: link,
transclude: 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) ->
user = @auth.getUser()
@.user = @auth.userData
@appTitle.set(user.username)
@appTitle.set(@.user.get('username'))
angular.module("taigaProfile").controller("Profile", ProfilePageController)

View File

@ -23,7 +23,7 @@ describe "ProfileController", ->
stub = sinon.stub()
mocks.auth = {
getUser: sinon.stub()
userData: Immutable.fromJS({username: "UserName"})
}
provide.value "$tgAuth", mocks.auth
@ -44,14 +44,14 @@ describe "ProfileController", ->
inject ($controller) ->
controller = $controller
it "define projects", () ->
user = {
username: "UserName"
}
mocks.auth.getUser.returns(user)
it "define user", () ->
ctrl = controller "Profile",
$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.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-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-projects)

View File

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

View File

@ -1,4 +1,4 @@
describe "ProfileBar", ->
describe "ProjectController", ->
$controller = null
$q = null
provide = null
@ -19,6 +19,13 @@ describe "ProfileBar", ->
provide.value "$appTitle", mocks.appTitle
_mockAuth = () ->
mocks.auth = {
userData: Immutable.fromJS({username: "UserName"})
}
provide.value "$tgAuth", mocks.auth
_mockRouteParams = () ->
provide.value "$routeParams", {
pslug: "project-slug"
@ -30,6 +37,7 @@ describe "ProfileBar", ->
_mockProjectsService()
_mockRouteParams()
_mockAppTitle()
_mockAuth()
return null
@ -44,6 +52,18 @@ describe "ProfileBar", ->
_mocks()
_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", () ->
project = Immutable.fromJS({
name: "projectName"
@ -59,8 +79,6 @@ describe "ProfileBar", ->
thenStub.callArg(0, project)
$rootScope.$apply()
expect(mocks.appTitle.set.withArgs("projectName")).to.be.calledOnce
@ -79,6 +97,4 @@ describe "ProfileBar", ->
thenStub.callArg(0, project)
$rootScope.$apply()
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.project-data
section.timeline
div(tg-user-timeline)
section.timeline(ng-if="vm.project")
div(tg-user-timeline, projectId="vm.project.get('id')", userId="vm.user.get('id')")
section.involved-data
h2.title {{"PROJECT.SECTION.TEAM" | translate}}
ul.involved-team

View File

@ -22,6 +22,17 @@ Resource = (urlsService, http) ->
url = urlsService.resolve("bulk-update-projects-order")
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 {"projects": service}

View File

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

View File

@ -49,6 +49,12 @@ timelineType = (timeline, event) ->
key: 'TIMELINE.TASK_CREATED',
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
check: (timeline, event) ->
return timeline.data.comment && event.obj == 'userstory'

View File

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

View File

@ -1,5 +1,5 @@
describe "UserTimelineController", ->
myCtrl = scope = $q = provide = null
controller = scope = $q = provide = null
mocks = {}
@ -7,42 +7,37 @@ describe "UserTimelineController", ->
_mockUserTimeline = () ->
mocks.userTimelineService = {
getTimeline: sinon.stub()
getTimeline: sinon.stub(),
getProjectTimeline: sinon.stub()
}
provide.value "tgUserTimelineService", mocks.userTimelineService
_mockTgAuth = () ->
provide.value "$tgAuth", {
getUser: () ->
return mockUser
}
_mocks = () ->
module ($provide) ->
provide = $provide
_mockUserTimeline()
_mockTgAuth()
return null
beforeEach ->
module "taigaUserTimeline"
_mocks()
inject ($controller, _$q_) ->
$q = _$q_
myCtrl = $controller "UserTimeline"
controller = $controller
it "timelineList should be an array", () ->
myCtrl = controller "UserTimeline"
expect(myCtrl.timelineList.toJS()).is.an("array")
it "pagination starts at 1", () ->
myCtrl = controller "UserTimeline"
expect(myCtrl.page).to.be.equal(1)
describe "load timeline", () ->
thenStub = timelineList = null
timelineList = null
beforeEach () ->
timelineList = Immutable.fromJS([
@ -52,6 +47,10 @@ describe "UserTimelineController", ->
{ fake: "fake"}
])
it "the loadingData variable must be true during the timeline load", () ->
myCtrl = controller "UserTimeline"
myCtrl.userId = mockUser.id
thenStub = sinon.stub()
mocks.userTimelineService.getTimeline = sinon.stub()
@ -60,7 +59,6 @@ describe "UserTimelineController", ->
then: thenStub
})
it "the loadingData variable must be true during the timeline load", () ->
expect(myCtrl.loadingData).to.be.false
myCtrl.loadTimeline()
@ -72,6 +70,17 @@ describe "UserTimelineController", ->
expect(myCtrl.loadingData).to.be.false
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)
myCtrl.loadTimeline()
@ -81,8 +90,39 @@ describe "UserTimelineController", ->
expect(myCtrl.page).to.equal(2)
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()
thenStub.callArgWith(0, timelineList)
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",
controller: "UserTimeline",
controllerAs: "vm",
scope: {}
scope: {
projectId: "=projectid",
userId: "=userid"
},
bindToController: true
}
angular.module("taigaProfile").directive("tgUserTimeline", UserTimelineDirective)

View File

@ -55,4 +55,10 @@ class UserTimelineService extends taiga.Service
.then (result) =>
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)

View File

@ -12,6 +12,10 @@ describe "tgUserTimelineService", ->
getTimeline: sinon.stub()
}
mocks.resources.projects = {
getTimeline: sinon.stub()
}
provide.value "tgResources", mocks.resources
_mocks = () ->
@ -36,74 +40,74 @@ describe "tgUserTimelineService", ->
_setup()
_inject()
it "filter invalid timeline items", (done) ->
valid_items = [
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
"status": "xx",
"subject": "xx"
}
valid_items = [
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
"status": "xx",
"subject": "xx"
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake": "xx"
}
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake": "xx"
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake2": "xx"
}
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake2": "xx"
}
},
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake2": "xx",
"milestone": "xx"
}
}
},
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
"fake2": "xx",
"milestone": "xx"
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
attachments: {
new: []
}
}
}
},
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
attachments: {
new: [1, 2]
}
}
}
},
{ # invalid item
event_type: "xx.tt.delete",
data: {
values_diff: {
attachments: {
new: [1, 2]
}
}
},
{ # invalid item
event_type: "xx.tt.create",
data: {
values_diff: {
attachments: {
new: []
}
}
}
]
},
{ # valid item
event_type: "xx.tt.create",
data: {
values_diff: {
attachments: {
new: [1, 2]
}
}
}
},
{ # invalid item
event_type: "xx.tt.delete",
data: {
values_diff: {
attachments: {
new: [1, 2]
}
}
}
}
]
it "filter invalid user timeline items", (done) ->
userId = 3
page = 2
@ -126,3 +130,27 @@ describe "tgUserTimelineService", ->
done()
$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()