new resources return immutable objects

stable
Juanfran 2015-05-12 13:26:55 +02:00
parent 0b5d09ca0c
commit 042d73b771
33 changed files with 462 additions and 134 deletions

View File

@ -66,7 +66,17 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
)
$routeProvider.when("/project/:pslug/",
{templateUrl: "project/project.html"})
{
templateUrl: "projects/project/project-page.html",
resolve: {
loader: tgLoaderProvider.add(true),
pageParams: -> {
"authRequired": true
}
},
controller: "Page"
}
)
$routeProvider.when("/project/:pslug/search",
{templateUrl: "search/search.html", reloadOnSearch: false})

View File

@ -13,6 +13,16 @@
if (immutable_collection.toJS) {
collection = immutable_collection.toJS();
}
$scope[aliasAs] = collection; -> $scope[aliasAs] = immutable_collection;
value = collection[key];
immutable_value = immutable_collection.get(key); #x2
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
-> (x2)
updateScope(block.scope, index, valueIdentifier, immutable_value, keyIdentifier, key, collectionLength);
--copy from angular
copy angular hashKey
copy angular createMap
@ -205,7 +215,7 @@
nextBlockOrder,
elementsToRemove;
if (aliasAs) {
$scope[aliasAs] = collection;
$scope[aliasAs] = immutable_collection;
}
if (isArrayLike(collection)) {
collectionKeys = collection;
@ -226,6 +236,7 @@
for (index = 0; index < collectionLength; index++) {
key = (collection === collectionKeys) ? index : collectionKeys[index];
value = collection[key];
immutable_value = immutable_collection.get(key);
trackById = trackByIdFn(key, value, index);
if (lastBlockMap[trackById]) {
// found previously seen block
@ -265,6 +276,7 @@
for (index = 0; index < collectionLength; index++) {
key = (collection === collectionKeys) ? index : collectionKeys[index];
value = collection[key];
immutable_value = immutable_collection.get(key);
block = nextBlockOrder[index];
if (block.scope) {
// if we have already seen this object, then we need to reuse the
@ -279,7 +291,7 @@
$animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
}
previousNode = getBlockEnd(block);
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
updateScope(block.scope, index, valueIdentifier, immutable_value, keyIdentifier, key, collectionLength);
} else {
// new item which we don't know about
$transclude(function ngRepeatTransclude(clone, scope) {
@ -295,7 +307,7 @@
// by a directive with templateUrl when its template arrives.
block.clone = clone;
nextBlockMap[block.id] = block;
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
updateScope(block.scope, index, valueIdentifier, immutable_value, keyIdentifier, key, collectionLength);
});
}
}

View File

@ -1,18 +1,18 @@
DutyDirective = (navurls, projectsService, $translate) ->
DutyDirective = (navurls, $translate) ->
link = (scope, el, attrs, ctrl) ->
scope.vm = {}
scope.vm.duty = scope.duty
scope.vm.getDutyType = () ->
if scope.vm.duty
if scope.vm.duty._name == "userstories"
if scope.vm.duty.get('_name') == "userstories"
return $translate.instant("COMMON.USER_STORY")
if scope.vm.duty._name == "tasks"
if scope.vm.duty.get('_name') == "tasks"
return $translate.instant("COMMON.TASK")
if scope.vm.duty._name == "issues"
if scope.vm.duty.get('_name') == "issues"
return $translate.instant("COMMON.ISSUE")
directive = {
return {
templateUrl: "home/duties/duty.html"
scope: {
"duty": "=tgDuty"
@ -20,11 +20,8 @@ DutyDirective = (navurls, projectsService, $translate) ->
link: link
}
return directive
DutyDirective.$inject = [
"$tgNavUrls",
"tgProjectsService",
"$translate"
]

View File

@ -48,7 +48,7 @@ describe "dutyDirective", () ->
compile = $compile
it "duty directive scope content", () ->
scope.duty = {
scope.duty = Immutable.fromJS({
project: 1
ref: 1
_name: "userstories"
@ -56,7 +56,7 @@ describe "dutyDirective", () ->
photo: "http://jstesting.taiga.io/photo"
full_name_display: "Taiga testing js"
}
}
})
mockTgProjectsService.projectsById.get
.withArgs("1")
@ -72,4 +72,5 @@ describe "dutyDirective", () ->
elm = createDirective()
scope.$apply()
expect(elm.isolateScope().vm.getDutyType()).to.be.equal("User story translated")

View File

@ -1,13 +1,13 @@
a(href="{{ ::vm.duty.url }}", title="{{ ::duty.subject }}")
a(href="{{ ::vm.duty.get('url') }}", title="{{ ::duty.get('subject') }}")
img.avatar(
src="{{ ::vm.duty.assigned_to_extra_info.photo }}"
title="{{ ::vm.duty.assigned_to_extra_info.full_name_display }}")
src="{{ ::vm.duty.get('assigned_to_extra_info').get('photo') }}"
title="{{ ::vm.duty.get('assigned_to_extra_info').get('full_name_display') }}")
div.duty-data
div
span.duty-type {{ ::vm.getDutyType() }}
span.duty-status(ng-style="{'color': vm.duty.status_extra_info.color}") {{ ::vm.duty.status_extra_info.name }}
span.duty-status(ng-style="{'color': vm.duty.get('status_extra_info').get('color')}") {{ ::vm.duty.get('status_extra_info').get('name') }}
span.duty-title
span.duty-id(tg-bo-ref="duty.ref")
span.duty-name {{ ::duty.subject }}
div.duty-project {{ ::vm.duty.projectName}}
span.duty-id(tg-bo-ref="duty.get('ref')")
span.duty-name {{ ::duty.get('subject') }}
div.duty-project {{ ::vm.duty.get('projectName')}}

View File

@ -13,7 +13,7 @@ div.home-wrapper.centered
include ../../svg/hide.svg
p(translate="HOME.EMPTY_WATCHING")
section.watching(ng-show="vm.watching")
section.watching(ng-show="vm.watching.size")
div.duty-single(tg-duty="duty", tg-repeat="duty in vm.watching", ng-class="{blocked: duty.is_blocked}")
aside.project-list(tg-home-project-list)

View File

@ -1,5 +1,5 @@
class HomeService extends taiga.Service
@.$inject = ["$q", "$tgNavUrls", "$tgResources", "$rootScope", "$projectUrl", "$tgAuth"]
@.$inject = ["$q", "$tgNavUrls", "tgResources", "$rootScope", "$projectUrl", "$tgAuth"]
constructor: (@q, @navurls, @rs, @rootScope, @projectUrl, @auth) ->
@._workInProgress = Immutable.Map()
@ -11,27 +11,64 @@ class HomeService extends taiga.Service
@.fetchWorkInProgress()
attachProjectInfoToWorkInProgress: (projectsById) ->
_attachProjectInfoToDuty = (duty) =>
project = projectsById.get(String(duty.project))
_attachProjectInfoToDuty = (duty, objType) =>
project = projectsById.get(String(duty.get('project')))
ctx = {
project: project.slug
ref: duty.ref
project: project.get('slug')
ref: duty.get('ref')
}
duty.url = @navurls.resolve("project-#{duty._name}-detail", ctx)
duty.projectName = project.name
url = @navurls.resolve("project-#{objType}-detail", ctx)
duty = duty.set('url', url)
duty = duty.set('projectName', project.get('name'))
duty = duty.set("_name", objType)
return duty
@._workInProgress = Immutable.fromJS({
assignedTo: {
userStories: _.map(_.clone(@.assignedToUserStories), _attachProjectInfoToDuty)
tasks: _.map(_.clone(@.assignedToTasks), _attachProjectInfoToDuty)
issues: _.map(_.clone(@.assignedToIssues), _attachProjectInfoToDuty)
}
watching: {
userStories: _.map(_.clone(@.watchingUserStories), _attachProjectInfoToDuty)
tasks: _.map(_.clone(@.watchingTasks), _attachProjectInfoToDuty)
issues: _.map(_.clone(@.watchingIssues), _attachProjectInfoToDuty)
}
assignedTo = Immutable.Map()
if @.assignedToUserStories
_duties = @.assignedToUserStories.map (duty) ->
return _attachProjectInfoToDuty(duty, "userstories")
assignedTo = assignedTo.set("userStories", _duties)
if @.assignedToTasks
_duties = @.assignedToTasks.map (duty) ->
return _attachProjectInfoToDuty(duty, "tasks")
assignedTo = assignedTo.set("tasks", _duties)
if @.assignedToIssues
_duties = @.assignedToIssues.map (duty) ->
return _attachProjectInfoToDuty(duty, "issues")
assignedTo = assignedTo.set("issues", _duties)
watching = Immutable.Map()
if @.watchingUserStories
_duties = @.watchingUserStories.map (duty) ->
return _attachProjectInfoToDuty(duty, "userstories")
watching = watching.set("userStories", _duties)
if @.watchingTasks
_duties = @.watchingTasks.map (duty) ->
return _attachProjectInfoToDuty(duty, "tasks")
watching = watching.set("tasks", _duties)
if @.watchingIssues
_duties = @.watchingIssues.map (duty) ->
return _attachProjectInfoToDuty(duty, "issues")
watching = watching.set("issues", _duties)
@._workInProgress = Immutable.Map({
assignedTo: assignedTo,
watching: watching
})
getWorkInProgress: () ->

View File

@ -58,7 +58,7 @@ describe "tgHome", ->
then: mocks.thenStubWatchingIssues
})
provide.value "$tgResources", mocks.resources
provide.value "tgResources", mocks.resources
_mockProjectUrl = () ->
mocks.projectUrl = {get: sinon.stub()}
@ -83,7 +83,7 @@ describe "tgHome", ->
provide.value "$tgNavUrls", mocks.tgNavUrls
_inject = (callback) ->
inject (_$q_, _$tgResources_, _$rootScope_, _$projectUrl_, _$timeout_, _tgHomeService_) ->
inject (_$timeout_, _tgHomeService_) ->
timeout = _$timeout_
homeService = _tgHomeService_
callback() if callback
@ -107,12 +107,12 @@ describe "tgHome", ->
describe "fetch items", ->
it "work in progress filled", () ->
mocks.thenStubAssignedToUserstories.callArg(0, [{"id": 1}])
mocks.thenStubAssignedToTasks.callArg(0, [{"id": 2}])
mocks.thenStubAssignedToIssues.callArg(0, [{"id": 3}])
mocks.thenStubWatchingUserstories.callArg(0, [{"id": 4}])
mocks.thenStubWatchingTasks.callArg(0, [{"id": 5}])
mocks.thenStubWatchingIssues.callArg(0, [{"id": 6}])
mocks.thenStubAssignedToUserstories.callArg(0, Immutable.fromJS([{"id": 1}]))
mocks.thenStubAssignedToTasks.callArg(0, Immutable.fromJS([{"id": 2}]))
mocks.thenStubAssignedToIssues.callArg(0, Immutable.fromJS([{"id": 3}]))
mocks.thenStubWatchingUserstories.callArg(0, Immutable.fromJS([{"id": 4}]))
mocks.thenStubWatchingTasks.callArg(0, Immutable.fromJS([{"id": 5}]))
mocks.thenStubWatchingIssues.callArg(0, Immutable.fromJS([{"id": 6}]))
timeout.flush()
expect(homeService.workInProgress.toJS()).to.be.eql({
@ -136,23 +136,23 @@ describe "tgHome", ->
it "project info filled", () ->
duty = {
id: 66
_name: "userstories"
ref: 123
project: 1
}
mocks.thenStubAssignedToUserstories.callArg(0, [duty])
mocks.thenStubAssignedToTasks.callArg(0)
mocks.thenStubAssignedToIssues.callArg(0)
mocks.thenStubWatchingUserstories.callArg(0)
mocks.thenStubWatchingTasks.callArg(0)
mocks.thenStubWatchingIssues.callArg(0)
mocks.thenStubAssignedToUserstories.callArg(0, Immutable.fromJS([duty]))
mocks.thenStubAssignedToTasks.callArg(0, Immutable.fromJS([]))
mocks.thenStubAssignedToIssues.callArg(0, Immutable.fromJS([]))
mocks.thenStubWatchingUserstories.callArg(0, Immutable.fromJS([]))
mocks.thenStubWatchingTasks.callArg(0, Immutable.fromJS([]))
mocks.thenStubWatchingIssues.callArg(0, Immutable.fromJS([]))
timeout.flush()
projectsById = {
get: () -> {
get: () -> Immutable.fromJS({
name: "Testing project"
slug: "testing-project"
}
})
}
mocks.tgNavUrls.resolve
@ -160,6 +160,7 @@ describe "tgHome", ->
.returns("/testing-project/us/123")
homeService.attachProjectInfoToWorkInProgress(projectsById)
expect(homeService.workInProgress.toJS()).to.be.eql({
assignedTo: {
userStories: [

View File

@ -1,12 +1,12 @@
ul.home-project-list(ng-show="vm.projects.size")
li.home-project-list-single(tg-bind-scope, tg-repeat="project in vm.projects")
a(href="#", tg-nav="project:project=project.slug")
a(href="#", tg-nav="project:project=project.get('slug')")
h2.home-project-list-single-title
span.project-name(ng-bind="::project.name", title="{{ ::project.name }}")
span.private(ng-if="project.is_private", title="{{'PROJECT.PRIVATE' | translate}}")
span.project-name(title="{{ ::project.get('name') }}") {{::project.get('name')}}
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
include ../../../svg/lock.svg
p {{ ::project.description | limitTo:150 }}
span(ng-if="::project.description.length > 150") ...
p {{ ::project.get('description') | limitTo:150 }}
span(ng-if="::project.get('description').size > 150") ...
a.see-more-projects-btn.button-gray(ng-show="vm.projects.size", href="#", tg-nav="projects", title="{{'PROJECT.NAVIGATION.SEE_MORE_PROJECTS' | translate}}", translate="PROJECT.NAVIGATION.SEE_MORE_PROJECTS")
section.projects-empty(ng-hide="vm.projects.size")
include ../../../svg/empty-project.svg

View File

@ -5,8 +5,7 @@ div.navbar-dropdown.dropdown-project-list
ul
a(href="#",
tg-repeat="project in vm.projects",
ng-bind="::project.name"
tg-nav="project:project=project.slug")
tg-nav="project:project=project.get('slug')") {{::project.get("name")}}
a.see-more-projects-btn.button-gray(
href="#",

View File

@ -11,6 +11,6 @@ class ProfileBarController
loadStats: () ->
return @userService.getStats(@.user.id).then (stats) =>
@.stats = stats.toJS()
@.stats = stats
angular.module("taigaProfile").controller("ProfileBar", ProfileBarController)

View File

@ -58,4 +58,4 @@ describe "ProfileBar", ->
$rootScope.$apply()
expect(ctrl.stats).to.be.eql(stats.toJS())
expect(ctrl.stats.toJS()).to.be.eql(stats.toJS())

View File

@ -9,7 +9,7 @@ section.profile-bar
// span(translate="USER.PROFILE.FOLLOW")
div.profile-data
h1 {{::vm.user.full_name}}
h2 {{::vm.stats.roles.join(", ")}}
h2 {{::vm.stats.get('roles').join(", ")}}
// div.location
// include ../../../svg/location.svg
// span Madrid
@ -19,13 +19,13 @@ section.profile-bar
// These values in profile stats are not defined yet in UX. Please ask
div.profile-stats
div.stat
span.stat-number {{::vm.stats.projects}}
span.stat-number {{::vm.stats.get('projects')}}
span.stat-name(translate="USER.PROFILE.PROJECTS")
div.stat
span.stat-number {{::vm.stats.closed_userstories}}
span.stat-number {{::vm.stats.get('closed_userstories')}}
span.stat-name(translate="USER.PROFILE.CLOSED_US")
div.stat
span.stat-number {{::vm.stats.contacts}}
span.stat-number {{::vm.stats.get('contacts')}}
span.stat-name(translate="USER.PROFILE.CONTACTS")
// TODO Hide until organizations come
// div.profile-organizations
@ -36,5 +36,5 @@ section.profile-bar
// div.organization
// div.organization
div.profile-quote(ng-if="vm.user.bio")
div.profile-quote(ng-if="::vm.user.bio")
span {{::vm.user.bio}}

View File

@ -7,19 +7,19 @@ section.profile-contacts
div.profile-contact-single(tg-repeat="contact in ::vm.contacts")
div.profile-contact-picture
a(tg-nav="user-profile:username=contact.username", title="{{::contact.name }}")
img(ng-src='{{::contact.photo}}', alt='{{::contact.full_name}}')
a(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('name') }}")
img(ng-src="{{::contact.get('photo')}}", alt="{{::contact.get('full_name')}}")
div.profile-contact-data
h1
a(tg-nav="user-profile:username=contact.username", title="{{::contact.name }}")
| {{::contact.full_name}}
a(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('name') }}")
| {{::contact.get('full_name')}}
// span.your-contact Your contact
p(ng-if="contact.bio") {{::contact.bio}}
p(ng-if="contact.bio") {{::contact.get('bio')}}
div.extra-info
span.position {{::contact.roles.join(", ")}}
span.position {{::contact.get('roles').join(", ")}}
// span.location todo
// div.profile-project-stats
// div.stat-projects(title="2 projects")

View File

@ -4,16 +4,16 @@ section.profile-projects
div.project-list-single-title
h1
a(href="#", tg-nav="project:project=project.slug", title="{{ ::project.name }}") {{::project.name}}
p {{ ::project.description | limitTo:300 }}
a(href="#", tg-nav="project:project=project.get('slug')", title="{{ ::project.get('name') }}") {{::project.get('name')}}
p {{ ::project.get('description') | limitTo:300 }}
div.project-list-single-tags.tags-container
span.tag(style='border-left: 5px solid {{::tag.color}};', ng-repeat="tag in ::project.colorized_tags")
span.tag-name {{::tag.name}}
span.tag(style='border-left: 5px solid {{::tag.get("color")}};', tg-repeat="tag in ::project.get('colorized_tags')")
span.tag-name {{::tag.get('name')}}
div.project-list-single-members
a(ng-repeat="contact in ::project.contacts", tg-nav="user-profile:username=contact.username", title="{{::contact.full_name}}")
img(ng-src="{{::contact.photo}}")
a(tg-repeat="contact in ::project.get('contacts')", tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('full_name')}}")
img(ng-src="{{::contact.get('photo')}}")
// div.project-list-single-right
// div.project-list-single-stats

View File

@ -6,7 +6,7 @@ class ProfileTimelineItemController
]
constructor: (@sce, @profileTimelineItemType, @profileTimelineItemTitle) ->
timeline = @.timeline
timeline = @.timeline.toJS()
event = @.parseEventType(timeline.event_type)
type = @profileTimelineItemType.getType(timeline, event)

View File

@ -71,8 +71,9 @@ describe "ProfileTimelineItemController", ->
it "basic activity fields filled", () ->
timeline = scope.vm.timeline
timeline_immutable = Immutable.fromJS(timeline)
myCtrl = controller("ProfileTimelineItem", {$scope: scope}, {timeline: timeline})
myCtrl = controller("ProfileTimelineItem", {$scope: scope}, {timeline: timeline_immutable})
expect(myCtrl.activity.user).to.be.equal(timeline.data.user)
expect(myCtrl.activity.project).to.be.equal(timeline.data.project)
@ -94,7 +95,9 @@ describe "ProfileTimelineItemController", ->
mockType.description.withArgs(timeline).returns(description)
mockType.member.withArgs(timeline).returns(member)
myCtrl = controller("ProfileTimelineItem", {$scope: scope}, {timeline: timeline})
timeline_immutable = Immutable.fromJS(timeline)
myCtrl = controller("ProfileTimelineItem", {$scope: scope}, {timeline: timeline_immutable})
expect(myCtrl.activity.description).to.be.an('object') # $sce.trustAsHtml
expect(myCtrl.activity.member).to.be.equal(member)

View File

@ -19,7 +19,7 @@ ProjectsListingDirective = (projectsService) ->
sorted_project_ids = _.map(scope.vm.projects.toArray(), (p) -> p.id)
sorted_project_ids = _.without(sorted_project_ids, project.id)
sorted_project_ids.splice(index, 0, project.id)
sorted_project_ids.splice(index, 0, project.get('id'))
sortData = []
for value, index in sorted_project_ids
sortData.push({"project_id": value, "order":index})

View File

@ -15,15 +15,15 @@ div.project-list-wrapper.centered
div.project-list-single-left
div.project-list-single-title
h1
a(href="#", tg-nav="project:project=project.slug")
h1.project-name(ng-bind="::project.name", title="{{ ::project.name }}")
span.private(ng-if="project.is_private", title="{{'PROJECT.PRIVATE' | translate}}")
a(href="#", tg-nav="project:project=project.get('slug')")
h1.project-name(title="{{ ::project.get('name') }}") {{project.get('name')}}
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
include ../../../svg/lock.svg
p {{ ::project.description | limitTo:300 }}
span(ng-if="::project.description.length > 300") ...
p {{ ::project.get('description') | limitTo:300 }}
span(ng-if="::project.get('description').length > 300") ...
div.project-list-single-tags.tags-container(ng-if="::project.tags")
div.tags-block(tg-colorize-tags="project.tags", tg-colorize-tags-type="backlog")
div.project-list-single-tags.tags-container(ng-if="::project.get('tags').size")
div.tags-block(tg-colorize-tags="project.get('tags')", tg-colorize-tags-type="backlog")
div.project-list-single-right
span.drag.icon.icon-drag-v

View File

@ -0,0 +1 @@
div(tg-project)

View File

@ -0,0 +1,17 @@
class ProjectController
@.$inject = [
"tgProjectsService",
"$routeParams",
"$appTitle"
]
constructor: (@projectsService, @routeParams, @appTitle) ->
projectSlug = @routeParams.pslug
@projectsService.getProjectBySlug(projectSlug)
.then (project) =>
@appTitle.set(project.get("name"))
@.project = project
angular.module("taigaProjects").controller("Project", ProjectController)

View File

@ -0,0 +1,84 @@
describe "ProfileBar", ->
$controller = null
$q = null
provide = null
$rootScope = null
mocks = {}
_mockProjectsService = () ->
mocks.projectService = {
getProjectBySlug: sinon.stub()
}
provide.value "tgProjectsService", mocks.projectService
_mockAppTitle = () ->
mocks.appTitle = {
set: sinon.stub()
}
provide.value "$appTitle", mocks.appTitle
_mockRouteParams = () ->
provide.value "$routeParams", {
pslug: "project-slug"
}
_mocks = () ->
module ($provide) ->
provide = $provide
_mockProjectsService()
_mockRouteParams()
_mockAppTitle()
return null
_inject = (callback) ->
inject (_$controller_, _$q_, _$rootScope_) ->
$q = _$q_
$rootScope = _$rootScope_
$controller = _$controller_
beforeEach ->
module "taigaProjects"
_mocks()
_inject()
it "set page title", () ->
project = Immutable.fromJS({
name: "projectName"
})
thenStub = sinon.stub()
mocks.projectService.getProjectBySlug.withArgs("project-slug").returns({
then: thenStub
})
ctrl = $controller("Project")
thenStub.callArg(0, project)
$rootScope.$apply()
expect(mocks.appTitle.set.withArgs("projectName")).to.be.calledOnce
it "set local project variable", () ->
project = Immutable.fromJS({
name: "projectName"
})
thenStub = sinon.stub()
mocks.projectService.getProjectBySlug.withArgs("project-slug").returns({
then: thenStub
})
ctrl = $controller("Project")
thenStub.callArg(0, project)
$rootScope.$apply()
expect(ctrl.project).to.be.equal(project)

View File

@ -0,0 +1,8 @@
ProjectDirective = () ->
return {
templateUrl: "projects/project/project.html",
controllerAs: "vm",
controller: "Project"
}
angular.module("taigaProjects").directive("tgProject", ProjectDirective)

View File

@ -0,0 +1,24 @@
div.wrapper
div.main.centered.single-project
section.single-project-intro
h1
span.green(class="project-name") {{::vm.project.get("name")}}
span.private(ng-if="::vm.project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
include ../../../svg/lock.svg
p.description {{vm.project.get('description')}}
div.project-list-single-tags.tags-container(ng-if="::vm.project.get('tags').size")
div.tags-block(tg-colorize-tags="vm.project.get('tags')", tg-colorize-tags-type="backlog")
div.project-data
section.timeline
span TODO. Missing the amazing timeline around!!dfdfdf
section.involved-data
h2.title Team
ul.involved-team
a(href="", title="{{::member.get('full_name')}}", tg-repeat="member in ::vm.project.get('memberships')")
img(ng-src="{{::member.get('photo')}}", alt="{{::member.get('full_name')}}")
// h2.title Organizations
// div.involved-organization
// a(href="", title="User Name")
// img(src="https://s3.amazonaws.com/uifaces/faces/twitter/dan_higham/48.jpg", alt="{{member.full_name}}")

View File

@ -2,7 +2,7 @@ taiga = @.taiga
groupBy = @.taiga.groupBy
class ProjectsService extends taiga.Service
@.$inject = ["$tgResources", "$rootScope", "$projectUrl", "tgLightboxFactory"]
@.$inject = ["tgResources", "$rootScope", "$projectUrl", "tgLightboxFactory"]
constructor: (@rs, @rootScope, @projectUrl, @lightboxFactory) ->
@._currentUserProjects = Immutable.Map()
@ -18,30 +18,40 @@ class ProjectsService extends taiga.Service
getCurrentUserProjects: ->
return @._currentUserProjectsPromise
getProjectBySlug: (projectSlug) ->
return @rs.projects.getProjectBySlug(projectSlug)
getProjectStats: (projectId) ->
return @rs.projects.getProjectStats(projectId)
fetchProjects: ->
if not @._inProgress
@._inProgress = true
@._currentUserProjectsPromise = @rs.projects.listByMember(@rootScope.user?.id)
@._currentUserProjectsPromise = @rs.users.getProjects(@rootScope.user?.id)
@._currentUserProjectsPromise.then (projects) =>
_.map projects, (project) =>
project.url = @projectUrl.get(project)
projects = projects.map (project) =>
url = @projectUrl.get(project.toJS())
project.colorized_tags = []
project = project.set("url", url)
colorized_tags = []
if project.tags
tags = project.tags.sort()
if project.get("tags")
tags = project.get("tags").sort()
project.colorized_tags = _.map tags, (tag) ->
color = project.tags_colors[tag]
colorized_tags = tags.map (tag) ->
color = project.get("tags_colors").get(tag)
return {name: tag, color: color}
@._currentUserProjects = Immutable.fromJS({
all: projects,
recents: projects.slice(0, 10)
})
project = project.set("colorized_tags", colorized_tags)
@._currentUserProjectsById = Immutable.fromJS(groupBy(projects, (p) -> p.id))
return project
@._currentUserProjects = @._currentUserProjects.set("all", projects)
@._currentUserProjects = @._currentUserProjects.set("recents", projects.slice(0, 10))
@._currentUserProjectsById = Immutable.fromJS(groupBy(projects.toJS(), (p) -> p.id))
return @.projects

View File

@ -5,18 +5,19 @@ describe "tgProjects", ->
_mockResources = () ->
mocks.resources = {}
mocks.resources.projects = {
listByMember: sinon.stub()
mocks.resources.users = {
getProjects: sinon.stub()
}
mocks.thenStub = sinon.stub()
mocks.finallyStub = sinon.stub()
mocks.resources.projects.listByMember.withArgs(10).returns({
mocks.resources.users.getProjects.withArgs(10).returns({
then: mocks.thenStub
finally: mocks.finallyStub
})
provide.value "$tgResources", mocks.resources
provide.value "tgResources", mocks.resources
_mockRootScope = () ->
provide.value "$rootScope", {user: {id: 10}}
@ -64,22 +65,22 @@ describe "tgProjects", ->
beforeEach ->
projects = [
{"id": 1, tags: ['xx', 'yy', 'aa'], tags_colors: {xx: "red", yy: "blue", aa: "white"}},
{"id": 2},
{"id": 3},
{"id": 4},
{"id": 5},
{"id": 6},
{"id": 7},
{"id": 8},
{"id": 9},
{"id": 10},
{"id": 11},
{"id": 12},
{"id": 1, url: 'url-1', tags: ['xx', 'yy', 'aa'], tags_colors: {xx: "red", yy: "blue", aa: "white"}, colorized_tags: [{name: 'aa', color: 'white'}, {name: 'xx', color: 'red'}, {name: 'yy', color: 'blue'}]},
{"id": 2, url: 'url-2'},
{"id": 3, url: 'url-3'},
{"id": 4, url: 'url-4'},
{"id": 5, url: 'url-5'},
{"id": 6, url: 'url-6'},
{"id": 7, url: 'url-7'},
{"id": 8, url: 'url-8'},
{"id": 9, url: 'url-9'},
{"id": 10, url: 'url-10'},
{"id": 11, url: 'url-11'},
{"id": 12, url: 'url-12'}
]
it "all & recents filled", () ->
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
expect(projectsService.currentUserProjects.get("all").toJS()).to.be.eql(projects)
expect(projectsService.currentUserProjects.get("recents").toJS()).to.be.eql(projects.slice(0, 10))
@ -87,7 +88,7 @@ describe "tgProjects", ->
it "_inProgress change to false when tgResources end", () ->
expect(projectsService._inProgress).to.be.true
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
mocks.finallyStub.callArg(0)
expect(projectsService._inProgress).to.be.false
@ -96,25 +97,25 @@ describe "tgProjects", ->
projectsService.fetchProjects()
projectsService.fetchProjects()
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
expect(mocks.resources.projects.listByMember).have.been.calledOnce
expect(mocks.resources.users.getProjects).have.been.calledOnce
it "group projects by id", () ->
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
expect(projectsService.currentUserProjectsById.size).to.be.equal(12)
expect(projectsService.currentUserProjectsById.toJS()[1].id).to.be.equal(projects[0].id)
it "add urls in the project object", () ->
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
expect(projectsService.currentUserProjectsById.toJS()[1].url).to.be.equal("url-1")
expect(projectsService.currentUserProjects.get("all").toJS()[0].url).to.be.equal("url-1")
expect(projectsService.currentUserProjects.get("recents").toJS()[0].url).to.be.equal("url-1")
it "add sorted colorized_tags project object", () ->
mocks.thenStub.callArg(0, projects)
mocks.thenStub.callArg(0, Immutable.fromJS(projects))
tags = [
{name: "aa", color: "white"},
@ -152,6 +153,7 @@ describe "tgProjects", ->
thenStub = sinon.stub()
mocks.resources.projects = {}
mocks.resources.projects.bulkUpdateOrder = sinon.stub()
mocks.resources.projects.bulkUpdateOrder.withArgs(projects_order).returns({
then: thenStub
@ -164,3 +166,21 @@ describe "tgProjects", ->
thenStub.callArg(0)
expect(projectsService.fetchProjects).to.have.been.calledOnce
it "getProjectBySlug", () ->
projectSlug = "project-slug"
mocks.resources.projects = {}
mocks.resources.projects.getProjectBySlug = sinon.stub()
mocks.resources.projects.getProjectBySlug.withArgs(projectSlug).returns(true)
expect(projectsService.getProjectBySlug(projectSlug)).to.be.true
it "getProjectStats", () ->
projectId = 3
mocks.resources.projects = {}
mocks.resources.projects.getProjectStats = sinon.stub()
mocks.resources.projects.getProjectStats.withArgs(projectId).returns(true)
expect(projectsService.getProjectStats(projectId)).to.be.true

View File

@ -0,0 +1,23 @@
Resource = (urlsService, http) ->
service = {}
service.listInAllProjects = (params) ->
url = urlsService.resolve("issues")
httpOptions = {
headers: {
"x-disable-pagination": "1"
}
}
return http.get(url, params, httpOptions)
.then (result) ->
return Immutable.fromJS(result.data)
return () ->
return {"issues": service}
Resource.$inject = ["$tgUrls", "$tgHttp"]
module = angular.module("taigaResources2")
module.factory("tgIssuesResource", Resource)

View File

@ -0,0 +1,31 @@
Resource = (urlsService, http) ->
service = {}
service.getProjectBySlug = (projectSlug) ->
url = urlsService.resolve("projects")
url = "#{url}/by_slug?slug=#{projectSlug}"
return http.get(url)
.then (result) ->
return Immutable.fromJS(result.data)
service.getProjectStats = (projectId) ->
url = urlsService.resolve("projects")
url = "#{url}/#{projectId}"
return http.get(url)
.then (result) ->
return Immutable.fromJS(result.data)
service.bulkUpdateOrder = (bulkData) ->
url = urlsService.resolve("bulk-update-projects-order")
return http.post(url, bulkData)
return () ->
return {"projects": service}
Resource.$inject = ["$tgUrls", "$tgHttp"]
module = angular.module("taigaResources2")
module.factory("tgProjectsResources", Resource)

View File

@ -1,5 +1,9 @@
services = [
"tgProjectsResources"
"tgProjectsResources",
"tgUsersResources",
"tgUserstoriesResource",
"tgTasksResource",
"tgIssuesResource"
]
Resources = ($injector) ->

View File

@ -0,0 +1,23 @@
Resource = (urlsService, http) ->
service = {}
service.listInAllProjects = (params) ->
url = urlsService.resolve("tasks")
httpOptions = {
headers: {
"x-disable-pagination": "1"
}
}
return http.get(url, params, httpOptions)
.then (result) ->
return Immutable.fromJS(result.data)
return () ->
return {"tasks": service}
Resource.$inject = ["$tgUrls", "$tgHttp"]
module = angular.module("taigaResources2")
module.factory("tgTasksResource", Resource)

View File

@ -43,4 +43,4 @@ Resource = (urlsService, http) ->
Resource.$inject = ["$tgUrls", "$tgHttp"]
module = angular.module("taigaResources2")
module.factory("tgProjectsResources", Resource)
module.factory("tgUsersResources", Resource)

View File

@ -0,0 +1,23 @@
Resource = (urlsService, http) ->
service = {}
service.listInAllProjects = (params) ->
url = urlsService.resolve("userstories")
httpOptions = {
headers: {
"x-disable-pagination": "1"
}
}
return http.get(url, params, httpOptions)
.then (result) ->
return Immutable.fromJS(result.data)
return () ->
return {"userstories": service}
Resource.$inject = ["$tgUrls", "$tgHttp"]
module = angular.module("taigaResources2")
module.factory("tgUserstoriesResource", Resource)

View File

@ -32,7 +32,7 @@ module.exports = function(config) {
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'**/*.coffee': ['coffee'],
'**/*.spec.coffee': ['coffee'],
'dist/js/app.js': ['sourcemap']
},