Initial integration of userstory page

stable
Alejandro Alonso 2014-07-10 14:57:30 +02:00
parent 9f078257e2
commit 8dd8e632b1
10 changed files with 296 additions and 60 deletions

View File

@ -26,8 +26,17 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide,
$routeProvider.when("/project/:pslug/backlog", {templateUrl: "/partials/backlog.html"}) $routeProvider.when("/project/:pslug/backlog", {templateUrl: "/partials/backlog.html"})
$routeProvider.when("/project/:pslug/taskboard/:id", {templateUrl: "/partials/taskboard.html"}) $routeProvider.when("/project/:pslug/taskboard/:id", {templateUrl: "/partials/taskboard.html"})
$routeProvider.when("/project/:pslug/issues", {templateUrl: "/partials/issues.html"})
$routeProvider.when("/project/:pslug/search", {templateUrl: "/partials/search.html"}) $routeProvider.when("/project/:pslug/search", {templateUrl: "/partials/search.html"})
# User stories
$routeProvider.when("/project/:pslug/us/:usref",
{templateUrl: "/partials/us-detail.html"})
$routeProvider.when("/project/:pslug/us/:usref/edit",
{templateUrl: "/partials/us-detail-edit.html"})
# Issues
$routeProvider.when("/project/:pslug/issues", {templateUrl: "/partials/issues.html"})
$routeProvider.when("/project/:pslug/issues/:issueref", $routeProvider.when("/project/:pslug/issues/:issueref",
{templateUrl: "/partials/issues-detail.html"}) {templateUrl: "/partials/issues-detail.html"})
@ -95,6 +104,7 @@ modules = [
"taigaBacklog", "taigaBacklog",
"taigaTaskboard", "taigaTaskboard",
"taigaIssues", "taigaIssues",
"taigaUserStories",
"taigaSearch", "taigaSearch",
"taigaAdmin", "taigaAdmin",
"taigaNavMenu", "taigaNavMenu",

View File

@ -59,6 +59,9 @@ urls = {
"project-issues-detail": "/project/:project/issues/:ref", "project-issues-detail": "/project/:project/issues/:ref",
"project-issues-detail-edit": "/project/:project/issues/:ref/edit", "project-issues-detail-edit": "/project/:project/issues/:ref/edit",
"project-userstories-detail": "/project/:project/us/:ref",
"project-userstories-detail-edit": "/project/:project/us/:ref/edit",
# Admin # Admin
"project-admin-home": "/project/:project/admin/project-profile/details", "project-admin-home": "/project/:project/admin/project-profile/details",
"project-admin-project-profile-details": "/project/:project/admin/project-profile/details" "project-admin-project-profile-details": "/project/:project/admin/project-profile/details"

View File

@ -69,6 +69,7 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
loadIssue: -> loadIssue: ->
return @rs.issues.get(@scope.projectId, @scope.issueId).then (issue) => return @rs.issues.get(@scope.projectId, @scope.issueId).then (issue) =>
@scope.issue = issue @scope.issue = issue
@scope.commentModel = issue
@scope.previousUrl = "/project/#{@scope.project.slug}/issues/#{@scope.issue.neighbors.previous.ref}" if @scope.issue.neighbors.previous.id? @scope.previousUrl = "/project/#{@scope.project.slug}/issues/#{@scope.issue.neighbors.previous.ref}" if @scope.issue.neighbors.previous.id?
@scope.nextUrl = "/project/#{@scope.project.slug}/issues/#{@scope.issue.neighbors.next.ref}" if @scope.issue.neighbors.next.id? @scope.nextUrl = "/project/#{@scope.project.slug}/issues/#{@scope.issue.neighbors.next.ref}" if @scope.issue.neighbors.next.id?

View File

@ -24,6 +24,9 @@ taiga = @.taiga
resourceProvider = ($repo, $http, $urls) -> resourceProvider = ($repo, $http, $urls) ->
service = {} service = {}
service.get = (projectId, usId) ->
return $repo.queryOne("userstories", usId)
service.listUnassigned = (projectId) -> service.listUnassigned = (projectId) ->
params = {"project": projectId, "milestone": "null"} params = {"project": projectId, "milestone": "null"}
return $repo.queryMany("userstories", params) return $repo.queryMany("userstories", params)
@ -38,6 +41,9 @@ resourceProvider = ($repo, $http, $urls) ->
params = {projectId: projectId, bulkStories: data} params = {projectId: projectId, bulkStories: data}
return $http.post(url, params) return $http.post(url, params)
service.history = (usId) ->
return $repo.queryOneRaw("history/userstory", usId)
return (instance) -> return (instance) ->
instance.userstories = service instance.userstories = service

View File

@ -0,0 +1,22 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# 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: modules/us.coffee
###
module = angular.module("taigaUserStories", [])

View File

@ -0,0 +1,202 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# 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: modules/userstories/detail.coffee
###
taiga = @.taiga
mixOf = @.taiga.mixOf
groupBy = @.taiga.groupBy
module = angular.module("taigaUserStories")
#############################################################################
## User story Detail Controller
#############################################################################
class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
"$routeParams",
"$q",
"$location"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location) ->
@scope.issueRef = @params.issueref
@scope.sectionName = "User Story"
promise = @.loadInitialData()
promise.then null, ->
console.log "FAIL" #TODO
loadProject: ->
return @rs.projects.get(@scope.projectId).then (project) =>
@scope.project = project
@scope.statusList = project.issue_statuses
@scope.statusById = groupBy(project.us_statuses, (x) -> x.id)
@scope.severityList = project.severities
@scope.severityById = groupBy(project.severities, (x) -> x.id)
@scope.priorityList = project.priorities
@scope.priorityById = groupBy(project.priorities, (x) -> x.id)
@scope.membersById = groupBy(project.memberships, (x) -> x.user)
return project
loadUs: ->
return @rs.userstories.get(@scope.projectId, @scope.usId).then (us) =>
@scope.us = us
@scope.commentModel = us
@scope.previousUrl = "/project/#{@scope.project.slug}/us/#{@scope.us.neighbors.previous.ref}" if @scope.us.neighbors.previous.id?
@scope.nextUrl = "/project/#{@scope.project.slug}/us/#{@scope.us.neighbors.next.ref}" if @scope.us.neighbors.next.id?
loadHistory: ->
return @rs.userstories.history(@scope.usId).then (history) =>
_.each history.results, (historyResult) ->
#If description was modified take only the description_html field
if historyResult.values_diff.description?
historyResult.values_diff.description = historyResult.values_diff.description_html
delete historyResult.values_diff.description_html
delete historyResult.values_diff.description_diff
@scope.history = history.results
@scope.comments = _.filter(history.results, (historyEntry) -> historyEntry.comment != "")
loadInitialData: ->
params = {
pslug: @params.pslug
usref: @params.usref
}
promise = @repo.resolve(params).then (data) =>
@scope.projectId = data.project
@scope.usId = data.us
return data
return promise.then(=> @.loadProject())
.then(=> @.loadUsersAndRoles())
.then(=> @.loadUs())
.then(=> @.loadHistory())
module.controller("UserStoryDetailController", UserStoryDetailController)
#############################################################################
## User story Main Directive
#############################################################################
UsDirective = ($tgrepo, $log, $location, $confirm) ->
linkSidebar = ($scope, $el, $attrs, $ctrl) ->
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
linkSidebar($scope, $el, $attrs, $ctrl)
$el.on "click", ".save-us", (event) ->
$tgrepo.save($scope.us).then ->
$confirm.notify("success")
$location.path("/project/#{$scope.project.slug}/us/#{$scope.us.ref}")
$el.on "click", ".add-comment a.button-green", (event) ->
event.preventDefault()
$tgrepo.save($scope.us).then ->
$ctrl.loadHistory()
$el.on "click", ".us-activity-tabs li a", (event) ->
$el.find(".us-activity-tabs li a").toggleClass("active")
$el.find(".us-activity section").toggleClass("hidden")
return {link:link}
module.directive("tgUsDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", UsDirective])
#############################################################################
## User story status directive
#############################################################################
UsStatusDetailDirective = () ->
#TODO: i18n
template = _.template("""
<h1>
<span>
<% if (status.is_closed) { %>
Closed
<% } else { %>
Open
<% } %>
<span class="us-detail-status"><%= status.name %></span>
</h1>
<div class="issue-data">
<div class="status-data <% if (editable) { %>clickable<% } %>">
<span class="level" style="background-color:<%= status.color %>"></span>
<span class="status-status"><%= status.name %></span>
<span class="level-name">status</span>
</div>
</div>
""")
selectionStatusTemplate = _.template("""
<ul class="popover pop-status">
<% _.each(statuses, function(status) { %>
<li><a href="" class="status" title="<%- status.name %>"
data-status-id="<%- status.id %>"><%- status.name %></a></li>
<% }); %>
</ul>
""")
link = ($scope, $el, $attrs, $model) ->
editable = $attrs.editable?
renderUsstatus = (us) ->
status = $scope.statusById[us.status]
html = template({
editable: editable
status: status
})
$el.html(html)
$el.find(".status-data").append(selectionStatusTemplate({statuses:$scope.statusList}))
$scope.$watch $attrs.ngModel, (us) ->
if us?
renderUsstatus(us)
if editable
$el.on "click", ".status-data", (event) ->
event.preventDefault()
event.stopPropagation()
$el.find(".pop-status").show()
body = angular.element("body")
body.one "click", (event) ->
$el.find(".popover").hide()
$el.on "click", ".status", (event) ->
event.preventDefault()
event.stopPropagation()
target = angular.element(event.currentTarget)
$model.$modelValue.status = target.data("status-id")
renderUsstatus($model.$modelValue)
$el.find(".popover").hide()
return {link:link, require:"ngModel"}
module.directive("tgUsStatusDetail", UsStatusDetailDirective)

View File

@ -1,39 +1,35 @@
extends layout extends dummy-layout
block head block head
title Taiga Project management web application with scrum in mind! title Taiga Project management web application with scrum in mind!
block content block content
div.wrapper div.wrapper(tg-us-detail, ng-controller="UserStoryDetailController as ctrl",
ng-init="section='backlog'")
div.main.us-detail div.main.us-detail
div.us-detail-header div.us-detail-header
h1 include views/components/mainTitle
span ProjectName a.button.button-green(href="", title="Edit", tg-nav="project-userstories-detail-edit:project=project.slug,ref=us.ref") Edit
span.green User Story
a.button.button-green(href="", title="Edit") Edit
section.us-story-main-data section.us-story-main-data
h2.us-title div.us-title
span.us-number 125 h2.us-title-text
span.us-name Tagear contenido dentro de las catas privadas span.us-number(tg-bo-html="us.ref")
div.blocked-warning span.us-name(ng-bind="us.subject")
div.issue-nav
a.icon.icon-arrow-left(ng-show="nextUrl", href="{{ nextUrl }}", title="next user story")
a.icon.icon-arrow-right(ng-show="previousUrl",href="{{ previousUrl }}", title="previous user story")
div.blocked-warning(ng-show="us.is_blocked")
span.icon.icon-warning span.icon.icon-warning
p.blocked Blocked! p.blocked Blocked!
p We need Pilar to make a prototype out of this or we are not sure what to show at this part. p(tg-bind-html="us.blocked_note || 'This user story is blocked'")
a.button.button-red.button-block(href="", title="Unblock US") Unblock
div.user-story-tags div.user-story-tags(tg-tag-line, ng-model="us.tags")
- for(var y = 0; y < 6; y++)
include views/components/tag section.us-content.wysiwyg(tg-bind-html="us.description_html")
input(type="text", placeholder="Add Tag")
section.us-content //include views/modules/attachments
p Hay que cambiar el texto "Hola NombreDelUsuario" por "Nivel de conexion al XX%"
p La propuesta que esperábamos de UX debía incluir nombre y nivel de conexión. Esperamos nueva propuesta corregida donde aparezca tanto el nombre como el % de conexión
p <strong>Test de aceptación</strong>
ul
li Entro en la aplicación
li Compruebo que el indicador crece
p <strong>Prototipos</strong><br /><a href="">share.axure.com/lalala</a>
p <strong>Prototipos</strong><br /><a href="">share.axure.com/lalala</a>
include views/modules/attachments
section.us-activity section.us-activity
ul.us-activity-tabs ul.us-activity-tabs
li li
@ -45,36 +41,31 @@ block content
a(href="#") a(href="#")
span.icon.icon-issues span.icon.icon-issues
span.tab-title Activity span.tab-title Activity
//-include views/modules/comments include views/modules/comments
include views/modules/activity include views/modules/activity
sidebar.menu-secondary.sidebar sidebar.menu-secondary.sidebar
h1 section.us-status(tg-us-status-detail, ng-model="us")
span Open
span.us-detail-status In progress div.us-detail-progress-bar
div.us-detail-progress-bar div.current-progress
div.current-progress span.tasks-completed 6/7 tasks completed
span.tasks-completed 6/7 tasks completed ul.points-per-role
ul.points-per-role li.total
li.total span.points 10
span.points 10 span.role total
span.role total - for(var x=0; x<5; x++)
- for(var x=0; x<5; x++) li.total
li.total span.points 10
span.points 10 span.role UX
span.role UX
include views/components/assigned-to section.us-assigned-to(tg-assigned-to, ng-model="us")
section.watchers section.watchers(tg-watchers, ng-model="us.watchers")
div.watchers-header
span.title watchers section.us-detail-settings
a.icon.icon-plus(href="", title="Add watcher") span.button.button-gray(href="", title="Client requirement") Client requirement
div.watchers-content span.button.button-gray(href="", title="Team requirement") Team requirement
- for(var y=0; y<5; y++) span.button.button-red(href="", title="Block") Block
div.watcher-single
div.watcher-avatar div.lightbox.lightbox_block.hidden
a.avatar(href="", title="Assigned to") include views/modules/lightbox_block
img(src="http://thecodeplayer.com/u/uifaces/32.jpg", alt="username")
a.watcher-name(href="", title="Jesús Espino") Jesús Espino
section.us-detail-settings
span.button.button-gray(href="", title="Client requirement") Client requirement
span.button.button-gray(href="", title="Team requirement") Team requirement
span.button.button-red(href="", title="Block") Block

View File

@ -4,7 +4,7 @@ div.row.us-item-row(ng-repeat="us in visibleUserstories|orderBy:order track by u
span.tag(ng-repeat="tag in us.tags") {{ tag }} span.tag(ng-repeat="tag in us.tags") {{ tag }}
div.user-story-name div.user-story-name
input(type="checkbox", name="") input(type="checkbox", name="")
a(href="", title="{{ us.subject }}") {{ us.subject }} a.clickable(tg-nav="project-userstories-detail:project=project.slug,ref=us.ref", title="{{ us.subject }}") {{ us.subject }}
span.us-settings span.us-settings
a.icon.icon-edit(href="", ng-click="ctrl.editUserStory(us)", title="Edit") a.icon.icon-edit(href="", ng-click="ctrl.editUserStory(us)", title="Edit")
a.icon.icon-delete(href="", ng-click="ctrl.deleteUserStory(us)", title="Delete") a.icon.icon-delete(href="", ng-click="ctrl.deleteUserStory(us)", title="Delete")

View File

@ -1,6 +1,6 @@
section.us-comments section.us-comments
div.add-comment div.add-comment
textarea(placeholder="Write here a new commet", ng-model="issue.comment") textarea(placeholder="Write here a new commet", ng-model="commentModel.comment")
a.button.button-green(href="", title="Comment") Comment a.button.button-green(href="", title="Comment") Comment
div.comment-list div.comment-list
div.comment-single(tg-comment, ng-repeat="comment in comments") div.comment-single(tg-comment, ng-repeat="comment in comments")

View File

@ -43,6 +43,7 @@ paths = {
"app/coffee/modules/backlog/*.coffee", "app/coffee/modules/backlog/*.coffee",
"app/coffee/modules/taskboard/*.coffee", "app/coffee/modules/taskboard/*.coffee",
"app/coffee/modules/issues/*.coffee", "app/coffee/modules/issues/*.coffee",
"app/coffee/modules/userstories/*.coffee",
"app/coffee/modules/admin/*.coffee", "app/coffee/modules/admin/*.coffee",
"app/coffee/modules/locales/*.coffee", "app/coffee/modules/locales/*.coffee",
"app/coffee/modules/base/*.coffee", "app/coffee/modules/base/*.coffee",