new resources return immutable objects
parent
0b5d09ca0c
commit
042d73b771
|
@ -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})
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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')}}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: () ->
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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="#",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
div(tg-project)
|
|
@ -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)
|
|
@ -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)
|
|
@ -0,0 +1,8 @@
|
|||
ProjectDirective = () ->
|
||||
return {
|
||||
templateUrl: "projects/project/project.html",
|
||||
controllerAs: "vm",
|
||||
controller: "Project"
|
||||
}
|
||||
|
||||
angular.module("taigaProjects").directive("tgProject", ProjectDirective)
|
|
@ -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}}")
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -1,5 +1,9 @@
|
|||
services = [
|
||||
"tgProjectsResources"
|
||||
"tgProjectsResources",
|
||||
"tgUsersResources",
|
||||
"tgUserstoriesResource",
|
||||
"tgTasksResource",
|
||||
"tgIssuesResource"
|
||||
]
|
||||
|
||||
Resources = ($injector) ->
|
||||
|
|
|
@ -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)
|
|
@ -43,4 +43,4 @@ Resource = (urlsService, http) ->
|
|||
Resource.$inject = ["$tgUrls", "$tgHttp"]
|
||||
|
||||
module = angular.module("taigaResources2")
|
||||
module.factory("tgProjectsResources", Resource)
|
||||
module.factory("tgUsersResources", Resource)
|
||||
|
|
|
@ -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)
|
|
@ -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']
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue