epics pagination

stable
juanfran 2016-10-20 12:29:35 +02:00 committed by David Barragán Merino
parent 451b9f979c
commit 344bbc6325
7 changed files with 138 additions and 28 deletions

View File

@ -41,6 +41,8 @@ class EpicsTableController
} }
taiga.defineImmutableProperty @, 'epics', () => return @epicsService.epics taiga.defineImmutableProperty @, 'epics', () => return @epicsService.epics
taiga.defineImmutableProperty @, 'disabledEpicsPagination', () => return @epicsService._disablePagination
taiga.defineImmutableProperty @, 'loadingEpics', () => return @epicsService._loadingEpics
toggleEpicTableOptions: () -> toggleEpicTableOptions: () ->
@.displayOptions = !@.displayOptions @.displayOptions = !@.displayOptions
@ -50,6 +52,9 @@ class EpicsTableController
.then null, () => # on error .then null, () => # on error
@confirm.notify("error") @confirm.notify("error")
nextPage: () ->
@epicsService.nextPage()
hoverEpicTableOption: () -> hoverEpicTableOption: () ->
if @.timer if @.timer
@timeout.cancel(@.timer) @timeout.cancel(@.timer)

View File

@ -32,6 +32,7 @@ describe "EpicTable", ->
_mockTgEpicsService = () -> _mockTgEpicsService = () ->
mocks.tgEpicsService = { mocks.tgEpicsService = {
createEpic: sinon.stub() createEpic: sinon.stub()
nextPage: sinon.stub()
} }
provide.value "tgEpicsService", mocks.tgEpicsService provide.value "tgEpicsService", mocks.tgEpicsService
@ -55,3 +56,10 @@ describe "EpicTable", ->
epicTableCtrl.displayOptions = true epicTableCtrl.displayOptions = true
epicTableCtrl.toggleEpicTableOptions() epicTableCtrl.toggleEpicTableOptions()
expect(epicTableCtrl.displayOptions).to.be.false expect(epicTableCtrl.displayOptions).to.be.false
it "next page", () ->
epicTableCtrl = controller "EpicsTableCtrl"
epicTableCtrl.nextPage()
expect(mocks.tgEpicsService.nextPage).to.have.been.calledOnce

View File

@ -86,7 +86,13 @@ mixin epicSwitch(name, model)
for="switch-progress" for="switch-progress"
) )
+epicSwitch('switch-progress', 'vm.column.progress') +epicSwitch('switch-progress', 'vm.column.progress')
.epics-table-body(tg-epics-sortable="vm.reorderEpic(epic, newIndex)")
.epics-table-body(
tg-epics-sortable="vm.reorderEpic(epic, newIndex)"
infinite-scroll="vm.nextPage()"
infinite-scroll-disabled="vm.disabledEpicsPagination"
infinite-scroll-immediate-check="false"
)
.epics-table-body-row( .epics-table-body-row(
tg-repeat="epic in vm.epics track by epic.get('id')" tg-repeat="epic in vm.epics track by epic.get('id')"
tg-bind-scope tg-bind-scope
@ -95,3 +101,5 @@ mixin epicSwitch(name, model)
epic="epic" epic="epic"
column="vm.column" column="vm.column"
) )
div(tg-loading="vm.loadingEpics")

View File

@ -2,6 +2,15 @@
.epics-table { .epics-table {
margin-top: 2rem; margin-top: 2rem;
.loading {
margin: 2% auto;
width: 3rem;
img {
@include loading-spinner;
max-height: 3rem;
max-width: 3rem;
}
}
} }
.epics-table-header { .epics-table-header {

View File

@ -28,19 +28,39 @@ class EpicsService
] ]
constructor: (@projectService, @attachmentsService, @resources, @xhrError) -> constructor: (@projectService, @attachmentsService, @resources, @xhrError) ->
@._epics = Immutable.List() @.clear()
taiga.defineImmutableProperty @, 'epics', () => return @._epics taiga.defineImmutableProperty @, 'epics', () => return @._epics
clear: () -> clear: () ->
@._loadingEpics = false
@._disablePagination = false
@._page = 1
@._epics = Immutable.List() @._epics = Immutable.List()
fetchEpics: () -> fetchEpics: (reset = false) ->
return @resources.epics.list(@projectService.project.get('id')) @._loadingEpics = true
.then (epics) => @._disablePagination = true
@._epics = epics
return @resources.epics.list(@projectService.project.get('id'), @._page)
.then (result) =>
if reset
@.clear()
@._epics = result.list
else
@._epics = @._epics.concat(result.list)
@._loadingEpics = false
@._disablePagination = !result.headers('x-pagination-next')
.catch (xhr) => .catch (xhr) =>
@xhrError.response(xhr) @xhrError.response(xhr)
nextPage: () ->
@._page++
@.fetchEpics()
listRelatedUserStories: (epic) -> listRelatedUserStories: (epic) ->
return @resources.userstories.listInEpic(epic.get('id')) return @resources.userstories.listInEpic(epic.get('id'))
@ -52,8 +72,7 @@ class EpicsService
promises = _.map attachments.toJS(), (attachment) => promises = _.map attachments.toJS(), (attachment) =>
@attachmentsService.upload(attachment.file, epic.get('id'), epic.get('project'), 'epic') @attachmentsService.upload(attachment.file, epic.get('id'), epic.get('project'), 'epic')
Promise.all(promises).then () => Promise.all(promises).then(@.fetchEpics.bind(this, true))
@.fetchEpics()
reorderEpic: (epic, newIndex) -> reorderEpic: (epic, newIndex) ->
withoutMoved = @.epics.filter (it) => it.get('id') != epic.get('id') withoutMoved = @.epics.filter (it) => it.get('id') != epic.get('id')
@ -72,9 +91,8 @@ class EpicsService
epics_order: newOrder, epics_order: newOrder,
version: epic.get('version') version: epic.get('version')
} }
return @resources.epics.reorder(epic.get('id'), data, setOrders) return @resources.epics.reorder(epic.get('id'), data, setOrders)
.then () =>
@.fetchEpics()
reorderRelatedUserstory: (epic, epicUserstories, userstory, newIndex) -> reorderRelatedUserstory: (epic, epicUserstories, userstory, newIndex) ->
withoutMoved = epicUserstories.filter (it) => it.get('id') != userstory.get('id') withoutMoved = epicUserstories.filter (it) => it.get('id') != userstory.get('id')
@ -98,6 +116,13 @@ class EpicsService
.then () => .then () =>
return @.listRelatedUserStories(epic) return @.listRelatedUserStories(epic)
replaceEpic: (epic) ->
@._epics = @._epics.map (it) ->
if it.get('id') == epic.get('id')
return epic
return it
updateEpicStatus: (epic, statusId) -> updateEpicStatus: (epic, statusId) ->
data = { data = {
status: statusId, status: statusId,
@ -105,8 +130,7 @@ class EpicsService
} }
return @resources.epics.patch(epic.get('id'), data) return @resources.epics.patch(epic.get('id'), data)
.then () => .then(@.replaceEpic.bind(this))
@.fetchEpics()
updateEpicAssignedTo: (epic, userId) -> updateEpicAssignedTo: (epic, userId) ->
data = { data = {
@ -115,7 +139,6 @@ class EpicsService
} }
return @resources.epics.patch(epic.get('id'), data) return @resources.epics.patch(epic.get('id'), data)
.then () => .then(@.replaceEpic.bind(this))
@.fetchEpics()
angular.module('taigaEpics').service('tgEpicsService', EpicsService) angular.module('taigaEpics').service('tgEpicsService', EpicsService)

View File

@ -91,13 +91,50 @@ describe "tgEpicsService", ->
expect(epicsService._epics.size).to.be.equal(0) expect(epicsService._epics.size).to.be.equal(0)
it "fetch epics success", () -> it "fetch epics success", () ->
epics = Immutable.fromJS([ result = {}
result.list = Immutable.fromJS([
{ id: 111 } { id: 111 }
{ id: 112 } { id: 112 }
]) ])
promise = mocks.tgResources.epics.list.withArgs(1).promise().resolve(epics)
epicsService.fetchEpics().then () -> result.headers = () -> true
expect(epicsService.epics).to.be.equal(epics)
promise = mocks.tgResources.epics.list.withArgs(1).promise()
fetchPromise = epicsService.fetchEpics()
expect(epicsService._loadingEpics).to.be.true
expect(epicsService._disablePagination).to.be.true
promise.resolve(result)
fetchPromise.then () ->
expect(epicsService.epics).to.be.equal(result.list)
expect(epicsService._loadingEpics).to.be.false
expect(epicsService._disablePagination).to.be.false
it "fetch epics success, last page", () ->
result = {}
result.list = Immutable.fromJS([
{ id: 111 }
{ id: 112 }
])
result.headers = () -> false
promise = mocks.tgResources.epics.list.withArgs(1).promise()
fetchPromise = epicsService.fetchEpics()
expect(epicsService._loadingEpics).to.be.true
expect(epicsService._disablePagination).to.be.true
promise.resolve(result)
fetchPromise.then () ->
expect(epicsService.epics).to.be.equal(result.list)
expect(epicsService._loadingEpics).to.be.false
expect(epicsService._disablePagination).to.be.true
it "fetch epics error", () -> it "fetch epics error", () ->
epics = Immutable.fromJS([ epics = Immutable.fromJS([
@ -108,6 +145,23 @@ describe "tgEpicsService", ->
epicsService.fetchEpics().then () -> epicsService.fetchEpics().then () ->
expect(mocks.tgXhrErrorService.response.withArgs(new Error("error"))).have.been.calledOnce expect(mocks.tgXhrErrorService.response.withArgs(new Error("error"))).have.been.calledOnce
it "replace epic", () ->
epics = Immutable.fromJS([
{ id: 111 }
{ id: 112 }
])
epicsService._epics = epics
epic = Immutable.Map({
id: 112,
title: "title1"
})
epicsService.replaceEpic(epic)
expect(epicsService._epics.get(1)).to.be.equal(epic)
it "list related userstories", () -> it "list related userstories", () ->
epic = Immutable.fromJS({ epic = Immutable.fromJS({
id: 1 id: 1
@ -155,9 +209,9 @@ describe "tgEpicsService", ->
.promise() .promise()
.resolve() .resolve()
epicsService.fetchEpics = sinon.stub() epicsService.replaceEpic = sinon.stub()
epicsService.updateEpicStatus(epic, 33).then () -> epicsService.updateEpicStatus(epic, 33).then () ->
expect(epicsService.fetchEpics).have.been.calledOnce expect(epicsService.replaceEpic).have.been.calledOnce
it "Update epic assigned to", () -> it "Update epic assigned to", () ->
epic = Immutable.fromJS({ epic = Immutable.fromJS({
@ -171,9 +225,9 @@ describe "tgEpicsService", ->
.promise() .promise()
.resolve() .resolve()
epicsService.fetchEpics = sinon.stub() epicsService.replaceEpic = sinon.stub()
epicsService.updateEpicAssignedTo(epic, 33).then () -> epicsService.updateEpicAssignedTo(epic, 33).then () ->
expect(epicsService.fetchEpics).have.been.calledOnce expect(epicsService.replaceEpic).have.been.calledOnce
it "reorder epic", () -> it "reorder epic", () ->
epicsService._epics = Immutable.fromJS([ epicsService._epics = Immutable.fromJS([
@ -199,9 +253,7 @@ describe "tgEpicsService", ->
.promise() .promise()
.resolve() .resolve()
epicsService.fetchEpics = sinon.stub() epicsService.reorderEpic(epicsService._epics.get(2), 1)
epicsService.reorderEpic(epicsService._epics.get(2), 1).then () ->
expect(epicsService.fetchEpics).have.been.calledOnce
it "reorder related userstory in epic", () -> it "reorder related userstory in epic", () ->
epic = Immutable.fromJS({ epic = Immutable.fromJS({

View File

@ -33,13 +33,17 @@ Resource = (urlsService, http) ->
.then (result) -> .then (result) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
service.list = (projectId) -> service.list = (projectId, page=0) ->
url = urlsService.resolve("epics") url = urlsService.resolve("epics")
params = {project: projectId} params = {project: projectId, page: page}
return http.get(url, params) return http.get(url, params)
.then (result) -> Immutable.fromJS(result.data) .then (result) ->
return {
list: Immutable.fromJS(result.data)
headers: result.headers
}
service.patch = (id, patch) -> service.patch = (id, patch) ->
url = urlsService.resolve("epics") + "/#{id}" url = urlsService.resolve("epics") + "/#{id}"
@ -59,6 +63,7 @@ Resource = (urlsService, http) ->
options = {"headers": {"set-orders": JSON.stringify(setOrders)}} options = {"headers": {"set-orders": JSON.stringify(setOrders)}}
return http.patch(url, data, null, options) return http.patch(url, data, null, options)
.then (result) -> Immutable.fromJS(result.data)
service.addRelatedUserstory = (epicId, userstoryId) -> service.addRelatedUserstory = (epicId, userstoryId) ->
url = urlsService.resolve("epic-related-userstories", epicId) url = urlsService.resolve("epic-related-userstories", epicId)