Refactor epics module (need tests)
parent
da6cc67897
commit
99e04c369f
|
@ -812,6 +812,7 @@ modules = [
|
|||
"taigaHistory",
|
||||
"taigaWikiHistory",
|
||||
"taigaEpics",
|
||||
"taigaUtils"
|
||||
|
||||
# template cache
|
||||
"templates",
|
||||
|
|
|
@ -405,6 +405,7 @@
|
|||
},
|
||||
"EPICS": {
|
||||
"TITLE": "EPICS",
|
||||
"SECTION_NAME": "Epics",
|
||||
"EPIC": "EPIC",
|
||||
"DASHBOARD": {
|
||||
"ADD": "+ ADD EPIC",
|
||||
|
|
|
@ -24,13 +24,19 @@ getRandomDefaultColor = taiga.getRandomDefaultColor
|
|||
|
||||
class CreateEpicController
|
||||
@.$inject = [
|
||||
"tgResources"
|
||||
"$tgConfirm"
|
||||
"tgAttachmentsService"
|
||||
"$q"
|
||||
"tgProjectService",
|
||||
"tgEpicsService"
|
||||
]
|
||||
|
||||
constructor: (@rs, @confirm, @attachmentsService, @q) ->
|
||||
constructor: (@confirm, @projectService, @epicsService) ->
|
||||
# NOTE: To use Checksley setFormErrors() and validateForm()
|
||||
# are defined in the directive.
|
||||
|
||||
# NOTE: We use project as no inmutable object to make
|
||||
# the code compatible with the old code
|
||||
@.project = @projectService.project.toJS()
|
||||
|
||||
@.newEpic = {
|
||||
color: getRandomDefaultColor()
|
||||
project: @.project.id
|
||||
|
@ -39,25 +45,21 @@ class CreateEpicController
|
|||
}
|
||||
@.attachments = Immutable.List()
|
||||
|
||||
@.loading = false
|
||||
|
||||
createEpic: () ->
|
||||
return if not @.validateForm()
|
||||
|
||||
@.loading = true
|
||||
|
||||
promise = @rs.epics.post(@.newEpic)
|
||||
promise.then (response) =>
|
||||
@._createAttachments(response.data)
|
||||
promise.then (response) =>
|
||||
@.onCreateEpic()
|
||||
promise.then null, (response) =>
|
||||
@.setFormErrors(response.data)
|
||||
|
||||
if response.data._error_message
|
||||
confirm.notify("error", response.data._error_message)
|
||||
promise.finally () =>
|
||||
@.loading = false
|
||||
|
||||
return promise
|
||||
@epicsService.createEpic(@.epic, @.attachments)
|
||||
.then (response) => # On success
|
||||
@.onCreateEpic()
|
||||
.then null, (response) => # On error
|
||||
@.setFormErrors(response.data)
|
||||
if response.data._error_message
|
||||
@confirm.notify("error", response.data._error_message)
|
||||
@.loading = false
|
||||
|
||||
# Color selector
|
||||
selectColor: (color) ->
|
||||
|
@ -77,9 +79,4 @@ class CreateEpicController
|
|||
addAttachment: (attachment) ->
|
||||
@.attachments.push(attachment)
|
||||
|
||||
_createAttachments: (epic) ->
|
||||
promises = _.map @.attachments.toJS(), (attachment) =>
|
||||
return @attachmentsService.upload(attachment.file, epic.id, epic.project, 'epic')
|
||||
return @q.all(promises)
|
||||
|
||||
angular.module("taigaEpics").controller("CreateEpicCtrl", CreateEpicController)
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
# File: create-epic.directive.coffee
|
||||
###
|
||||
|
||||
module = angular.module('taigaEpics')
|
||||
|
||||
CreateEpicDirective = () ->
|
||||
link = (scope, el, attrs, ctrl) ->
|
||||
form = el.find("form").checksley()
|
||||
|
@ -35,12 +33,9 @@ CreateEpicDirective = () ->
|
|||
controller: "CreateEpicCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: {
|
||||
project: '=',
|
||||
onCreateEpic: '&'
|
||||
},
|
||||
scope: {}
|
||||
}
|
||||
|
||||
CreateEpicDirective.$inject = []
|
||||
|
||||
module.directive("tgCreateEpic", CreateEpicDirective)
|
||||
angular.module('taigaEpics').directive("tgCreateEpic", CreateEpicDirective)
|
||||
|
|
|
@ -33,11 +33,11 @@ tg-lightbox-close
|
|||
)
|
||||
fieldset.tags-block
|
||||
tg-tag-line-common(
|
||||
project="vm.project"
|
||||
tags="vm.newEpic.tags"
|
||||
permissions="add_epic"
|
||||
on-add-tag="vm.addTag(name, color)"
|
||||
on-delete-tag="vm.deleteTag(tag)"
|
||||
project="vm.project"
|
||||
tags="vm.newEpic.tags"
|
||||
permissions="add_epic"
|
||||
on-add-tag="vm.addTag(name, color)"
|
||||
on-delete-tag="vm.deleteTag(tag)"
|
||||
)
|
||||
fieldset
|
||||
textarea.e2e-create-epic-description(
|
||||
|
|
|
@ -17,19 +17,23 @@
|
|||
# File: epics-table.controller.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaEpics")
|
||||
|
||||
class EpicRowController
|
||||
@.$inject = [
|
||||
"tgResources",
|
||||
"$tgConfirm"
|
||||
"$tgConfirm",
|
||||
"tgProjectService",
|
||||
"tgEpicsService"
|
||||
]
|
||||
|
||||
constructor: (@rs, @confirm) ->
|
||||
constructor: (@confirm, @projectService, @epicsService) ->
|
||||
@.displayUserStories = false
|
||||
@.displayAssignedTo = false
|
||||
@.displayStatusList = false
|
||||
@.loadingStatus = false
|
||||
|
||||
# NOTE: We use project as no inmutable object to make
|
||||
# the code compatible with the old code
|
||||
@.project = @projectService.project.toJS()
|
||||
|
||||
_calculateProgressBar: () ->
|
||||
if @.epic.getIn(['status_extra_info', 'is_closed']) == true
|
||||
@.percentage = "100%"
|
||||
|
@ -42,68 +46,32 @@ class EpicRowController
|
|||
else
|
||||
@.percentage = "#{@.closed * 100 / @.total}%"
|
||||
|
||||
updateEpicStatus: (status) ->
|
||||
@.loadingStatus = true
|
||||
@.displayStatusList = false
|
||||
patch = {
|
||||
'status': status,
|
||||
'version': @.epic.get('version')
|
||||
}
|
||||
canEditEpics: () ->
|
||||
return @projectService.hasPermission("modify_epic")
|
||||
|
||||
onSuccess = =>
|
||||
@.loadingStatus = false
|
||||
@.onUpdateEpic()
|
||||
|
||||
onError = (data) =>
|
||||
@confirm.notify('error')
|
||||
|
||||
return @rs.epics.patch(@.epic.get('id'), patch).then(onSuccess, onError)
|
||||
|
||||
requestUserStories: (epic) ->
|
||||
toggleUserStoryList: () ->
|
||||
if !@.displayUserStories
|
||||
|
||||
onSuccess = (data) =>
|
||||
@.epicStories = data
|
||||
@.displayUserStories = true
|
||||
|
||||
onError = (data) =>
|
||||
@confirm.notify('error')
|
||||
|
||||
return @rs.userstories.listInEpic(@.epic.get('id')).then(onSuccess, onError)
|
||||
@epicsService.listRelatedUserStories(@.epic)
|
||||
.then (userStories) =>
|
||||
@.epicStories = userStories
|
||||
@.displayUserStories = true
|
||||
.catch =>
|
||||
@confirm.notify('error')
|
||||
else
|
||||
@.displayUserStories = false
|
||||
|
||||
onRemoveAssigned: () ->
|
||||
id = @.epic.get('id')
|
||||
version = @.epic.get('version')
|
||||
patch = {
|
||||
'assigned_to': null,
|
||||
'version': version
|
||||
}
|
||||
updateStatus: (statusId) ->
|
||||
@.displayStatusList = false
|
||||
@.loadingStatus = true
|
||||
return @epicsService.updateEpicStatus(@.epic, statusId)
|
||||
.catch () =>
|
||||
@confirm.notify('error')
|
||||
.finally () =>
|
||||
@.loadingStatus = false
|
||||
|
||||
onSuccess = =>
|
||||
@.onUpdateEpic()
|
||||
updateAssignedTo: (member) ->
|
||||
return @epicsService.updateEpicAssignedTo(@.epic, member?.id)
|
||||
.catch () =>
|
||||
@confirm.notify('error')
|
||||
|
||||
onError = (data) =>
|
||||
@confirm.notify('error')
|
||||
|
||||
return @rs.epics.patch(id, patch).then(onSuccess, onError)
|
||||
|
||||
onAssignTo: (member) ->
|
||||
id = @.epic.get('id')
|
||||
version = @.epic.get('version')
|
||||
patch = {
|
||||
'assigned_to': member.id,
|
||||
'version': version
|
||||
}
|
||||
|
||||
onSuccess = =>
|
||||
@.onUpdateEpic()
|
||||
@confirm.notify('success')
|
||||
|
||||
onError = (data) =>
|
||||
@confirm.notify('error')
|
||||
|
||||
return @rs.epics.patch(id, patch).then(onSuccess, onError)
|
||||
|
||||
module.controller("EpicRowCtrl", EpicRowController)
|
||||
angular.module("taigaEpics").controller("EpicRowCtrl", EpicRowController)
|
||||
|
|
|
@ -17,28 +17,16 @@
|
|||
# File: epics-table.directive.coffee
|
||||
###
|
||||
|
||||
module = angular.module('taigaEpics')
|
||||
|
||||
EpicRowDirective = () ->
|
||||
|
||||
link = (scope, el, attrs, ctrl) ->
|
||||
ctrl._calculateProgressBar()
|
||||
|
||||
return {
|
||||
link: link,
|
||||
templateUrl:"epics/dashboard/epic-row/epic-row.html",
|
||||
controller: "EpicRowCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: true,
|
||||
scope: {
|
||||
project: '=',
|
||||
epic: '=',
|
||||
column: '=',
|
||||
permissions: '=',
|
||||
onUpdateEpic: "&"
|
||||
}
|
||||
}
|
||||
|
||||
EpicRowDirective.$inject = []
|
||||
|
||||
module.directive("tgEpicRow", EpicRowDirective)
|
||||
angular.module('taigaEpics').directive("tgEpicRow", EpicRowDirective)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
.epic-row.e2e-epic-row(
|
||||
ng-class="{'is-blocked': vm.epic.get('is_blocked'), 'is-closed': vm.epic.get('is_closed'), 'unfold': vm.displayUserStories}"
|
||||
ng-click="vm.requestUserStories(vm.epic)"
|
||||
ng-click="vm.toggleUserStoryList()"
|
||||
)
|
||||
tg-svg.icon-drag(
|
||||
svg-icon="icon-drag"
|
||||
)
|
||||
|
||||
.vote(
|
||||
ng-if="vm.column.votes"
|
||||
ng-class="{'is-voter': vm.epic.get('is_voter')}"
|
||||
|
@ -28,23 +29,26 @@
|
|||
)
|
||||
|
||||
.project(ng-if="vm.column.project")
|
||||
.sprint(
|
||||
ng-if="vm.column.sprint"
|
||||
)
|
||||
.assigned.e2e-assigned-to
|
||||
|
||||
.sprint(ng-if="vm.column.sprint")
|
||||
|
||||
.assigned.e2e-assigned-tio(ng-if="vm.column.assigned")
|
||||
tg-assigned-to-component(
|
||||
assigned-to="vm.epic.get('assigned_to_extra_info')"
|
||||
project="vm.project"
|
||||
on-remove-assigned="vm.onRemoveAssigned()"
|
||||
on-assign-to="vm.onAssignTo(member)"
|
||||
on-remove-assigned="vm.updateAssignedTo(null)"
|
||||
on-assign-to="vm.updateAssignedTo(member)"
|
||||
tg-isolate-click
|
||||
)
|
||||
|
||||
.status(
|
||||
ng-if="vm.column.status && !vm.permissions.canEdit"
|
||||
ng-if="vm.column.status && !vm.canEditEpics()"
|
||||
)
|
||||
span {{vm.epic.getIn(['status_extra_info', 'name'])}}
|
||||
.status(
|
||||
ng-if="vm.column.status && vm.permissions.canEdit"
|
||||
ng-if="vm.column.status && vm.canEditEpics()"
|
||||
ng-mouseleave="vm.displayStatusList = false"
|
||||
tg-isolate-click
|
||||
)
|
||||
button(
|
||||
ng-click="vm.displayStatusList = true"
|
||||
|
@ -59,20 +63,19 @@
|
|||
ul.epic-statuses(ng-if="vm.displayStatusList")
|
||||
li.e2e-edit-epic-status(
|
||||
ng-repeat="status in vm.project.epic_statuses | orderBy:'order'"
|
||||
ng-click="vm.updateEpicStatus(status.id)"
|
||||
ng-click="vm.updateStatus(status.id)"
|
||||
) {{status.name}}
|
||||
|
||||
.progress(ng-if="vm.column.progress")
|
||||
.progress-bar
|
||||
.progress-status(
|
||||
ng-if="::vm.percentage"
|
||||
ng-style="{'width':vm.percentage}"
|
||||
)
|
||||
.epic-stories-wrapper(ng-if="vm.displayUserStories && vm.epicStories")
|
||||
|
||||
.epic-stories-wrapper(ng-if="vm.displayUserStories && vm.epicStories")
|
||||
.epic-story(tg-repeat="story in vm.epicStories track by story.get('id')")
|
||||
tg-story-row.e2e-story(
|
||||
epic="vm.epic"
|
||||
story="story"
|
||||
project="vm.project"
|
||||
column="vm.column"
|
||||
)
|
||||
|
|
|
@ -17,48 +17,50 @@
|
|||
# File: epics.dashboard.controller.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaEpics")
|
||||
taiga = @.taiga
|
||||
|
||||
|
||||
class EpicsDashboardController
|
||||
@.$inject = [
|
||||
"$tgResources",
|
||||
"tgResources",
|
||||
"$routeParams",
|
||||
"tgErrorHandlingService",
|
||||
"tgLightboxFactory",
|
||||
"lightboxService",
|
||||
"$tgConfirm"
|
||||
"$tgConfirm",
|
||||
"tgProjectService",
|
||||
"tgEpicsService"
|
||||
]
|
||||
|
||||
constructor: (@rs, @resources, @params, @errorHandlingService, @lightboxFactory, @lightboxService, @confirm) ->
|
||||
@.sectionName = "Epics"
|
||||
@.createEpic = false
|
||||
constructor: (@params, @errorHandlingService, @lightboxFactory, @lightboxService,
|
||||
@confirm, @projectService, @epicsService) ->
|
||||
|
||||
loadProject: () ->
|
||||
return @rs.projects.getBySlug(@params.pslug).then (project) =>
|
||||
if not project.is_epics_activated
|
||||
@errorHandlingService.permissionDenied()
|
||||
@.project = project
|
||||
@.loadEpics()
|
||||
@.sectionName = "EPICS.SECTION_NAME"
|
||||
|
||||
loadEpics: () ->
|
||||
projectId = @.project.id
|
||||
return @resources.epics.list(projectId).then (epics) =>
|
||||
@.epics = epics
|
||||
taiga.defineImmutableProperty @, 'project', () => return @projectService.project
|
||||
taiga.defineImmutableProperty @, 'epics', () => return @epicsService.epics
|
||||
|
||||
_onCreateEpic: () ->
|
||||
@lightboxService.closeAll()
|
||||
@confirm.notify("success")
|
||||
@.loadEpics()
|
||||
@._loadInitialData()
|
||||
|
||||
_loadInitialData: () ->
|
||||
@epicsService.clear()
|
||||
@projectService.setProjectBySlug(@params.pslug)
|
||||
.then () =>
|
||||
if not @.project.get("is_epics_activated") or not @projectService.hasPermission("view_epics")
|
||||
@errorHandlingService.permissionDenied()
|
||||
|
||||
@epicsService.fetchEpics()
|
||||
|
||||
canCreateEpics: () ->
|
||||
return @projectService.hasPermission("add_epic")
|
||||
|
||||
onCreateEpic: () ->
|
||||
@lightboxFactory.create('tg-create-epic', {
|
||||
"class": "lightbox lightbox-create-epic open"
|
||||
"project": "project"
|
||||
"on-create-epic": "onCreateEpic()"
|
||||
}, {
|
||||
"project": @.project
|
||||
"onCreateEpic": @._onCreateEpic.bind(this)
|
||||
"onCreateEpic": () =>
|
||||
@lightboxService.closeAll()
|
||||
@confirm.notify("success")
|
||||
})
|
||||
|
||||
module.controller("EpicsDashboardCtrl", EpicsDashboardController)
|
||||
angular.module("taigaEpics").controller("EpicsDashboardCtrl", EpicsDashboardController)
|
||||
|
|
|
@ -4,23 +4,20 @@
|
|||
header.header-with-actions
|
||||
h1(
|
||||
tg-main-title
|
||||
project-name="vm.project.name"
|
||||
i18n-section-name="{{ vm.sectionName }}"
|
||||
project-name="vm.project.get('name')"
|
||||
i18n-section-name="{{vm.sectionName}}"
|
||||
)
|
||||
.action-buttons(ng-if="vm.epics.size")
|
||||
.action-buttons(ng-if="vm.epics.size && vm.canCreateEpics()")
|
||||
button.button-green.e2e-create-epic(
|
||||
translate="EPICS.DASHBOARD.ADD"
|
||||
title="{{ EPICS.DASHBOARD.ADD_TITLE | translate }}",
|
||||
ng-click="vm.onCreateEpic()"
|
||||
)
|
||||
|
||||
|
||||
tg-epics-table(
|
||||
ng-if="vm.project && vm.epics.size"
|
||||
project="vm.project"
|
||||
epics="vm.epics"
|
||||
on-update-epic="vm.loadEpics()"
|
||||
ng-if="vm.epics.size"
|
||||
)
|
||||
|
||||
|
||||
section.empty-epics(ng-if="!vm.epics.size")
|
||||
img(
|
||||
src="/#{v}/images/epics-empty.png"
|
||||
|
@ -30,11 +27,12 @@
|
|||
p(translate="EPICS.EMPTY.EXPLANATION")
|
||||
a(
|
||||
translate="EPICS.EMPTY.HELP"
|
||||
href="https://tree.taiga.io/support/frequently-asked-questions/who-is-taiga-for/"
|
||||
href="#TODO: Link to Epics section in taiga-support"
|
||||
target="_blank"
|
||||
ng-title="EPICS.EMPTY.HELP | translate"
|
||||
)
|
||||
button.create-epic.button-green(
|
||||
ng-if="vm.canCreateEpics()"
|
||||
translate="EPICS.DASHBOARD.ADD"
|
||||
title="{{ EPICS.DASHBOARD.ADD_TITLE | translate }}"
|
||||
ng-click="vm.onCreateEpic()"
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
# File: epics-sortable.directive.coffee
|
||||
###
|
||||
|
||||
EpicsSortableDirective = ($parse) ->
|
||||
EpicsSortableDirective = ($parse, projectService) ->
|
||||
link = (scope, el, attrs) ->
|
||||
return if not projectService.hasPermission("modify_epic")
|
||||
|
||||
callback = $parse(attrs.tgEpicsSortable)
|
||||
|
||||
drake = dragula([el[0]], {
|
||||
|
@ -55,7 +57,8 @@ EpicsSortableDirective = ($parse) ->
|
|||
}
|
||||
|
||||
EpicsSortableDirective.$inject = [
|
||||
"$parse"
|
||||
"$parse",
|
||||
"tgProjectService"
|
||||
]
|
||||
|
||||
angular.module("taigaComponents").directive("tgEpicsSortable", EpicsSortableDirective)
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
# File: epics-table.controller.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaEpics")
|
||||
taiga = @.taiga
|
||||
|
||||
|
||||
class EpicsTableController
|
||||
@.$inject = []
|
||||
@.$inject = [
|
||||
"$tgConfirm",
|
||||
"tgEpicsService"
|
||||
]
|
||||
|
||||
constructor: () ->
|
||||
constructor: (@confirm, @epicsService) ->
|
||||
@.displayOptions = false
|
||||
@.displayVotes = true
|
||||
@.column = {
|
||||
|
@ -35,15 +39,14 @@ class EpicsTableController
|
|||
progress: true
|
||||
}
|
||||
|
||||
@.permissions = {
|
||||
canEdit: _.includes(@.project.my_permissions, 'modify_epic')
|
||||
}
|
||||
taiga.defineImmutableProperty @, 'epics', () => return @epicsService.epics
|
||||
|
||||
toggleEpicTableOptions: () ->
|
||||
@.displayOptions = !@.displayOptions
|
||||
|
||||
reorderEpic: (epic, newIndex) ->
|
||||
console.log epic, newIndex
|
||||
@epicsService.reorderEpic(epic, newIndex)
|
||||
.then null, () => # on error
|
||||
@confirm.notify("error")
|
||||
|
||||
|
||||
module.controller("EpicsTableCtrl", EpicsTableController)
|
||||
angular.module("taigaEpics").controller("EpicsTableCtrl", EpicsTableController)
|
||||
|
|
|
@ -17,21 +17,13 @@
|
|||
# File: epics-table.directive.coffee
|
||||
###
|
||||
|
||||
module = angular.module('taigaEpics')
|
||||
|
||||
EpicsTableDirective = () ->
|
||||
return {
|
||||
templateUrl:"epics/dashboard/epics-table/epics-table.html",
|
||||
controller: "EpicsTableCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: {
|
||||
epics: "=",
|
||||
project: "=",
|
||||
onUpdateEpic: "&"
|
||||
}
|
||||
scope: {}
|
||||
}
|
||||
|
||||
EpicsTableDirective.$inject = []
|
||||
|
||||
module.directive("tgEpicsTable", EpicsTableDirective)
|
||||
angular.module('taigaEpics').directive("tgEpicsTable", EpicsTableDirective)
|
||||
|
|
|
@ -95,8 +95,5 @@ mixin epicSwitch(name, model)
|
|||
)
|
||||
tg-epic-row.e2e-epic(
|
||||
epic="epic"
|
||||
project="vm.project"
|
||||
column="vm.column"
|
||||
on-update-epic="vm.onUpdateEpic()"
|
||||
permissions="vm.permissions"
|
||||
)
|
||||
|
|
|
@ -29,13 +29,8 @@ class StoryRowController
|
|||
if @.story.get('is_closed') == true
|
||||
@.percentage = "100%"
|
||||
else
|
||||
tasks = @.story.get('tasks').toJS()
|
||||
totalTasks = @.story.get('tasks').size
|
||||
areTasksCompleted = _.map(tasks, 'is_closed')
|
||||
totalTasksCompleted = _.pull(areTasksCompleted, false).length
|
||||
totalTasksCompleted = @.story.get('tasks').filter((it) -> it.get("is_closed")).size
|
||||
@.percentage = "#{totalTasksCompleted * 100 / totalTasks}%"
|
||||
|
||||
onSelectAssignedTo: () ->
|
||||
console.log 'ng-click="vm.onSelectAssignedTo()"'
|
||||
|
||||
module.controller("StoryRowCtrl", StoryRowController)
|
||||
|
|
|
@ -20,20 +20,15 @@
|
|||
module = angular.module('taigaEpics')
|
||||
|
||||
StoryRowDirective = () ->
|
||||
|
||||
return {
|
||||
templateUrl:"epics/dashboard/story-row/story-row.html",
|
||||
controller: "StoryRowCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: true,
|
||||
scope: {
|
||||
epic: '=',
|
||||
story: '=',
|
||||
project: '=',
|
||||
column: '='
|
||||
}
|
||||
}
|
||||
|
||||
StoryRowDirective.$inject = []
|
||||
|
||||
module.directive("tgStoryRow", StoryRowDirective)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.story-row(
|
||||
ng-class="{'is-blocked': vm.story.is_blocked, 'is-closed': vm.story.is_closed}"
|
||||
ng-class="{'is-blocked': vm.story.get('is_blocked'), 'is-closed': vm.story.get('is_closed')}"
|
||||
)
|
||||
.vote(
|
||||
ng-if="vm.column.votes"
|
||||
|
@ -11,12 +11,12 @@
|
|||
.name(ng-if="vm.column.name")
|
||||
- var hash = "#";
|
||||
a(
|
||||
tg-nav="project-userstories-detail:project=vm.project.slug,ref=vm.story.get('ref')"
|
||||
tg-nav="project-userstories-detail:project=vm.story.getIn(['project_extra_info', 'slug']),ref=vm.story.get('ref')"
|
||||
ng-attr-title="{{::vm.story.get('subject')}}"
|
||||
) #{hash}{{::vm.story.get('ref')}} {{::vm.story.get('subject')}}
|
||||
tg-belong-to-epics(
|
||||
format="pill"
|
||||
ng-if="vm.story.get('epics')"
|
||||
format="pill"
|
||||
epics="vm.story.get('epics')"
|
||||
)
|
||||
.project(
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
###
|
||||
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: epics.service.coffee
|
||||
###
|
||||
|
||||
taiga = @.taiga
|
||||
|
||||
class EpicsService
|
||||
@.$inject = [
|
||||
"tgProjectService",
|
||||
"tgAttachmentsService"
|
||||
"tgResources",
|
||||
"tgXhrErrorService",
|
||||
"$q"
|
||||
]
|
||||
|
||||
constructor: (@projectService, @attachmentsService, @resources, @xhrError, @q) ->
|
||||
@._epics = Immutable.List()
|
||||
taiga.defineImmutableProperty @, "epics", () => return @._epics
|
||||
|
||||
clear: () ->
|
||||
@._epics = Immutable.List()
|
||||
|
||||
fetchEpics: () ->
|
||||
return @resources.epics.list(@projectService.project.get("id"))
|
||||
.then (epics) =>
|
||||
@._epics = epics
|
||||
.catch (xhr) =>
|
||||
@xhrError.response(xhr)
|
||||
|
||||
listRelatedUserStories: (epic) ->
|
||||
return @resources.userstories.listInEpic(epic.get('id'))
|
||||
|
||||
createEpic: (epicData, attachments) ->
|
||||
@.epicData.project = @projectsService.project.id
|
||||
|
||||
return @resources.epics.post(@.epicData)
|
||||
.then (epic) =>
|
||||
promises = _.map attachments.toJS(), (attachment) =>
|
||||
@attachmentsService.upload(attachment.file, epic.get("id"), epic.get("project"), 'epic')
|
||||
@q.all(promises).then () =>
|
||||
@.fetchEpics()
|
||||
|
||||
reorderEpic: (epic, newIndex) ->
|
||||
withoutMoved = @.epics.filter (it) => it.get("id") != epic.get("id")
|
||||
beforeDestination = withoutMoved.slice(0, newIndex)
|
||||
|
||||
previous = beforeDestination.last()
|
||||
newOrder = if !previous then 0 else epic.get("epics_order") + 1
|
||||
|
||||
previousWithTheSameOrder = beforeDestination.filter (it) =>
|
||||
it.get("epics_order") == previous.get("epics_order")
|
||||
setOrders = Immutable.OrderedMap previousWithTheSameOrder.map (it) =>
|
||||
[it.get('id'), it.get("epics_order")]
|
||||
|
||||
data = {
|
||||
order: newOrder,
|
||||
version: epic.get("version")
|
||||
}
|
||||
|
||||
return @resources.epics.reorder(epic.get("id"), data, setOrders)
|
||||
.then () =>
|
||||
@.fetchEpics()
|
||||
|
||||
updateEpicStatus: (epic, statusId) ->
|
||||
data = {
|
||||
status: statusId,
|
||||
version: epic.get("version")
|
||||
}
|
||||
|
||||
return @resources.epics.patch(epic.get("id"), data)
|
||||
.then () =>
|
||||
@.fetchEpics()
|
||||
|
||||
updateEpicAssignedTo: (epic, userId) ->
|
||||
data = {
|
||||
assigned_to: userId,
|
||||
version: epic.get("version")
|
||||
}
|
||||
|
||||
return @resources.epics.patch(epic.get("id"), data)
|
||||
.then () =>
|
||||
@.fetchEpics()
|
||||
|
||||
angular.module("taigaEpics").service("tgEpicsService", EpicsService)
|
|
@ -51,6 +51,13 @@ Resource = (urlsService, http) ->
|
|||
|
||||
return http.post(url, params)
|
||||
|
||||
service.reorder = (id, data, setOrders) ->
|
||||
url = urlsService.resolve("epics") + "/#{id}"
|
||||
|
||||
options = {"headers": {"set-orders": JSON.stringify(setOrders)}}
|
||||
|
||||
return http.patch(url, data, null, options)
|
||||
|
||||
service.addRelatedUserstory = (epicId, userstoryId) ->
|
||||
url = urlsService.resolve("epic-related-userstories", epicId)
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
###
|
||||
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: isolate-click.directive.coffee
|
||||
###
|
||||
|
||||
IsolateClickDirective = () ->
|
||||
link = (scope, el, attrs) ->
|
||||
el.on 'click', (e) =>
|
||||
e.stopPropagation()
|
||||
|
||||
return {link: link}
|
||||
|
||||
angular.module("taigaUtils").directive("tgIsolateClick", IsolateClickDirective)
|
|
@ -0,0 +1,20 @@
|
|||
###
|
||||
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: utils.module.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaUtils", [])
|
Loading…
Reference in New Issue