project timeline
parent
dcc3e79f20
commit
25d3cdadce
|
@ -181,8 +181,9 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
},
|
||||
access: {
|
||||
requiresLogin: true
|
||||
}
|
||||
controller: "Profile"
|
||||
},
|
||||
controller: "Profile",
|
||||
controllerAs: "vm"
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -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}}",
|
||||
|
|
|
@ -2,7 +2,8 @@ ProfileBarDirective = () ->
|
|||
return {
|
||||
templateUrl: "profile/profile-bar/profile-bar.html",
|
||||
controller: "ProfileBar",
|
||||
controllerAs: "vm"
|
||||
controllerAs: "vm",
|
||||
scope: {}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
div(ng-if="tab.active")
|
||||
ng-transclude
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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 () ->
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue