diff --git a/.version b/.version
index 3ec7c5c4f..afd826259 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-v1.0.17
+v1.0.18
diff --git a/app/community/members.jade b/app/community/members.jade
index c3b356b47..e8c6f8dc3 100644
--- a/app/community/members.jade
+++ b/app/community/members.jade
@@ -28,8 +28,9 @@
small {{ctrl.currentMonth}}
.members-wrapper
.user-tile(ng-repeat="item in ctrl.memberLeaderboard")
- .avatar-wrapper: img(ng-src="{{item.avatar}}")
- .user-name {{item.name}}
+ a.avatar-wrapper(ui-sref="profile.about({userHandle: item.name})")
+ img(ng-src="{{item.avatar}}")
+ a.user-name(ui-sref="profile.about({userHandle: item.name})") {{item.name}}
.user-tag(class="{{item.class}}") {{item.contestType}}
p.user-desc {{item.description}}
a(ng-href="//www.{{ctrl.domain}}/community/member-programs/topcoder-member-of-the-month/", target="_blank").user-more Read the story
diff --git a/app/directives/challenge-user-place/challenge-user-place.directive.js b/app/directives/challenge-user-place/challenge-user-place.directive.js
index 402ce6f8e..23add31a8 100644
--- a/app/directives/challenge-user-place/challenge-user-place.directive.js
+++ b/app/directives/challenge-user-place/challenge-user-place.directive.js
@@ -59,7 +59,9 @@
$scope.imageURL = $scope.challenge.userDetails.submissions[0].submissionImage && $scope.challenge.userDetails.submissions[0].submissionImage.full;
$scope.selectedImage = $scope.imageURL;
- $scope.challenge.highestPlacement = _.max($scope.challenge.userDetails.submissions, 'placement').placement;
+ $scope.challenge.highestPlacement = _.min($scope.challenge.userDetails.submissions.filter(function(submission) {
+ return submission.type === CONSTANTS.SUBMISSION_TYPE_CONTEST && submission.placement;
+ }), 'placement').placement;
if ($scope.challenge.highestPlacement == 1) {
$scope.challenge.wonFirst = true;
diff --git a/app/directives/external-account/external-web-links.directive.js b/app/directives/external-account/external-web-links.directive.js
index 3367d474b..187d76e98 100644
--- a/app/directives/external-account/external-web-links.directive.js
+++ b/app/directives/external-account/external-web-links.directive.js
@@ -38,6 +38,9 @@
$log.debug("Web link added: " + JSON.stringify(data));
data.data.provider = data.provider;
$scope.linkedAccounts.push(data.data);
+ // reset the form when it is successfully added
+ $scope.addWebLinkFrm.$setPristine();
+ $scope.url = null;
toaster.pop('success', "Success", "Your link has been added. Data from your link will be visible on your profile shortly.");
})
.catch(function(resp) {
diff --git a/app/directives/external-account/external-web-links.directive.spec.js b/app/directives/external-account/external-web-links.directive.spec.js
index 6e8997e8b..3385a7706 100644
--- a/app/directives/external-account/external-web-links.directive.spec.js
+++ b/app/directives/external-account/external-web-links.directive.spec.js
@@ -90,24 +90,29 @@ describe('ExternalWebLinks Directive', function() {
});
expect(topcoderLink).to.exist;
expect(topcoderLink.status).to.exist.to.equal('PENDING');
+ expect(element.isolateScope().url).not.to.exist;
expect(toasterSvc.pop).to.have.been.calledWith('success').calledOnce;
});
it('should NOT add new weblink to linkedAccounts', function() {
+ var urlToAdd = 'https://www.topcoder.com';
element.isolateScope().userHandle = 'throwError';
- element.isolateScope().url = 'https://www.topcoder.com';
+ element.isolateScope().url = urlToAdd;
element.isolateScope().addWebLink();
scope.$digest();
expect(scope.linkedAccounts).to.have.length(1);
+ expect(element.isolateScope().url).to.exist.to.equal(urlToAdd);
expect(toasterSvc.pop).to.have.been.calledWith('error').calledOnce;
});
it('should NOT add new weblink to linkedAccounts', function() {
+ var urlToAdd = 'https://www.topcoder.com';
element.isolateScope().userHandle = 'alreadyExistsError';
- element.isolateScope().url = 'https://www.topcoder.com';
+ element.isolateScope().url = urlToAdd;
element.isolateScope().addWebLink();
scope.$digest();
expect(scope.linkedAccounts).to.have.length(1);
+ expect(element.isolateScope().url).to.exist.to.equal(urlToAdd);
expect(toasterSvc.pop).to.have.been.calledWith('error').calledOnce;
});
diff --git a/app/directives/srm-tile/srm-tile.directive.jade b/app/directives/srm-tile/srm-tile.directive.jade
index 9709b2ab4..fc6b92c95 100644
--- a/app/directives/srm-tile/srm-tile.directive.jade
+++ b/app/directives/srm-tile/srm-tile.directive.jade
@@ -3,7 +3,7 @@
.challenge-track
header
- a(ng-href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") {{srm.name}}
+ a(ng-href="https://community.{{DOMAIN}}/stat?c=round_overview&rd={{roundId}}") {{srm.name}}
.srm-details
p.starts-in Starts in #[span {{srm.codingStartAt | timeDiff:"quantity"}} {{srm.codingStartAt | timeDiff:'unit'}}]
@@ -17,22 +17,22 @@
.phase-status
.registered(ng-show="srm.userStatus === CONSTANTS.REGISTERED") Registered
.unregistered(ng-hide="srm.currentPhase !== CONSTANTS.REGISTRATION || srm.userStatus === CONSTANTS.REGISTERED")
- a.tc-btn.tc-btn-s.tc-btn-wide(href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") Register
+ a.tc-btn.tc-btn-s.tc-btn-wide(href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{roundId}}") Register
.past-srm(ng-show="srm.status === 'PAST'")
.challenge-track
header
- a(ng-href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") {{srm.name}}
+ a(ng-href="https://community.{{DOMAIN}}/stat?c=round_overview&rd={{roundId}}") {{srm.name}}
.ended-on #[span {{srm.codingEndAt | localTime:"MMM DD, YYYY" }}]
.member-stats
p.points #[span {{srm.result.finalPoints }}] #[span Points]
.ranks
- .division
+ a.division(href="https://community.{{DOMAIN}}/stat?c=round_stats&rd={{roundId}}&dn={{division}}")
p.rank {{srm.result.divisionPlacement}}
p.label #[span Division {{srm.result.division}}]
- .room
+ a.room(href="https://community.{{DOMAIN}}/stat?c=coder_room_stats&cr={{userId}}&rd={{roundId}}")
p.rank #[span {{srm.result.roomPlacement}}]
p.label #[span Room]
p.placement Placement
@@ -42,7 +42,7 @@
.challenge-track
header
.srm-name
- a(ng-href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") {{srm.name}}
+ a(ng-href="https://community.{{DOMAIN}}/stat?c=round_overview&rd={{roundId}}") {{srm.name}}
.srm-details
p.starts-in Starts in #[span {{srm.codingStartAt | timeDiff:"quantity"}} {{srm.codingStartAt | timeDiff:'unit'}}]
@@ -50,13 +50,13 @@
.phase-status
.registered(ng-show="srm.userStatus === CONSTANTS.REGISTERED") Registered
.unregistered(ng-hide="srm.currentPhase !== CONSTANTS.REGISTRATION || srm.userStatus === CONSTANTS.REGISTERED")
- a.tc-btn.tc-btn-s.tc-btn-wide(href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") Register
+ a.tc-btn.tc-btn-s.tc-btn-wide(href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{roundId}}") Register
.past-srm(ng-show="srm.status === 'PAST'")
.challenge-track
header
.srm-name
- a(ng-href="https://community.{{DOMAIN}}/tc?module=MatchDetails&rd={{srm.rounds[0].id}}") {{srm.name}}
+ a(ng-href="https://community.{{DOMAIN}}/stat?c=round_overview&rd={{roundId}}") {{srm.name}}
.srm-details
p.ended-on Ended {{srm.codingEndAt | timeDiff:"quantity"}} {{srm.codingEndAt | timeDiff:'unit'}} ago
@@ -65,10 +65,10 @@
p.points #[span {{srm.result.finalPoints }}] Points
.ranks
- .division
+ a.division(ng-class="{noclick: !srm.result.divisionPlacement}", href="https://community.{{DOMAIN}}/stat?c=round_stats&rd={{roundId}}&dn={{division}}")
p.rank {{srm.result.divisionPlacement}}
p.label #[span Div {{srm.result.division}}] #[span Placement]
- .room
+ a.room(ng-class="{noclick: !srm.result.roomPlacement}", href="https://community.{{DOMAIN}}/stat?c=coder_room_stats&cr={{userId}}&rd={{roundId}}")
p.rank #[span {{srm.result.roomPlacement}}]
p.label #[span Room] #[span Placement]
diff --git a/app/directives/srm-tile/srm-tile.directive.js b/app/directives/srm-tile/srm-tile.directive.js
index 85bf714b8..b5ab8afbc 100644
--- a/app/directives/srm-tile/srm-tile.directive.js
+++ b/app/directives/srm-tile/srm-tile.directive.js
@@ -8,14 +8,17 @@
srm: '=srm',
view: '=',
showResults: '=',
- showFooter: '='
+ showFooter: '=',
+ userId: '='
},
controller: ['$scope', '$filter', 'CONSTANTS', 'SRMService',
function($scope, $filter, CONSTANTS, SRMService) {
- $scope.DOMAIN = CONSTANTS.domain;
- $scope.CONSTANTS = CONSTANTS;
- $scope.srm.userStatus = _.get($scope.srm, 'userStatus', null);
- SRMService.processSRM($scope.srm);
+ $scope.DOMAIN = CONSTANTS.domain;
+ $scope.CONSTANTS = CONSTANTS;
+ $scope.srm.userStatus = _.get($scope.srm, 'userStatus', null);
+ SRMService.processSRM($scope.srm);
+ $scope.roundId = $scope.srm.rounds.length && $scope.srm.rounds[0].id;
+ $scope.division = $scope.srm.result && $scope.srm.result.division;
}]
};
});
diff --git a/app/my-dashboard/srms/srms.controller.js b/app/my-dashboard/srms/srms.controller.js
index 01c0ef967..3abf78fd5 100644
--- a/app/my-dashboard/srms/srms.controller.js
+++ b/app/my-dashboard/srms/srms.controller.js
@@ -12,6 +12,7 @@
var userId = UserService.getUserIdentity().userId;
var handle = UserService.getUserIdentity().handle;
+ vm.userId = userId;
activate();
diff --git a/app/my-dashboard/srms/srms.jade b/app/my-dashboard/srms/srms.jade
index 1133ed0b7..8bf637b6a 100644
--- a/app/my-dashboard/srms/srms.jade
+++ b/app/my-dashboard/srms/srms.jade
@@ -6,7 +6,7 @@ header
tc-section(state="vm.state")
.srm-tiles
- srm-tile(ng-repeat="srm in vm.srms", srm="srm", view="'tile'", ng-class="'tile-view'")
+ srm-tile(ng-repeat="srm in vm.srms", srm="srm", view="'tile'", ng-class="'tile-view'", user-id="vm.userId")
.srm-links-card
.flex-wrapper
diff --git a/app/my-srms/my-srms.controller.js b/app/my-srms/my-srms.controller.js
index b1422697e..965363983 100644
--- a/app/my-srms/my-srms.controller.js
+++ b/app/my-srms/my-srms.controller.js
@@ -28,6 +28,7 @@
var userId = UserService.getUserIdentity().userId;
var userHandle = UserService.getUserIdentity().handle;
+ vm.userId = userId;
vm.handle = userHandle;
activate();
diff --git a/app/my-srms/my-srms.jade b/app/my-srms/my-srms.jade
index fe042f3df..6a91b1314 100644
--- a/app/my-srms/my-srms.jade
+++ b/app/my-srms/my-srms.jade
@@ -23,6 +23,6 @@
.data(ng-class="vm.view + '-view'")
srm-tile.srm-tile(
ng-repeat="srm in vm.srms | orderBy:vm.orderBy:vm.reverseOrder",
- srm="srm", view="vm.view", ng-class="vm.view + '-view'", show-results="vm.statusFilter === 'past'")
+ srm="srm", view="vm.view", ng-class="vm.view + '-view'", show-results="vm.statusFilter === 'past'", user-id="vm.userId")
tc-endless-paginator(state="vm.loading", page-params="vm.pageParams")
diff --git a/app/profile/subtrack/data/data-challenges.jade b/app/profile/subtrack/data/data-challenges.jade
index 5055280de..e4f08fcc9 100644
--- a/app/profile/subtrack/data/data-challenges.jade
+++ b/app/profile/subtrack/data/data-challenges.jade
@@ -10,7 +10,7 @@
.challenges(ng-show="vm.subTrack == 'SRM'")
.challenge.tile(ng-repeat="challenge in vm.challenges")
- srm-tile(srm="challenge", domain="vm.domain", view="'tile'")
+ srm-tile(srm="challenge", domain="vm.domain", view="'tile'", user-id="profileVm.profile.userId")
.no-challenges(ng-show="!vm.challenges || vm.challenges.length == 0")
| Sorry, no rated SRMs found.
diff --git a/app/services/challenge.service.js b/app/services/challenge.service.js
index fddb91c48..8e4fe5c08 100644
--- a/app/services/challenge.service.js
+++ b/app/services/challenge.service.js
@@ -190,7 +190,7 @@
challenge.thumbnailId = challenge.userDetails.submissions[0].id;
challenge.highestPlacement = _.min(challenge.userDetails.submissions.filter(function(submission) {
- return submission.placement;
+ return submission.type === CONSTANTS.SUBMISSION_TYPE_CONTEST && submission.placement;
}), 'placement').placement;
if (challenge.highestPlacement == 1) {
diff --git a/app/skill-picker/skill-picker.controller.js b/app/skill-picker/skill-picker.controller.js
index e34eb5f2b..476fbb4b7 100644
--- a/app/skill-picker/skill-picker.controller.js
+++ b/app/skill-picker/skill-picker.controller.js
@@ -3,26 +3,123 @@
angular.module('tc.skill-picker').controller('SkillPickerController', SkillPickerController);
- SkillPickerController.$inject = ['CONSTANTS', 'ProfileService', '$state', 'userProfile', 'featuredSkills', '$log', 'toaster'];
+ SkillPickerController.$inject = ['$scope', 'CONSTANTS', 'ProfileService', '$state', 'userProfile', 'featuredSkills', '$log', 'toaster', 'MemberCertService', '$q'];
- function SkillPickerController(CONSTANTS, ProfileService, $state, userProfile, featuredSkills, $log, toaster) {
+ function SkillPickerController($scope, CONSTANTS, ProfileService, $state, userProfile, featuredSkills, $log, toaster, MemberCertService, $q) {
var vm = this;
$log = $log.getInstance("SkillPickerController");
vm.ASSET_PREFIX = CONSTANTS.ASSET_PREFIX;
+ vm.IOS_PROGRAM_ID = CONSTANTS.SWIFT_PROGRAM_ID;
vm.submitSkills = submitSkills;
vm.featuredSkills = featuredSkills;
+ vm.userId = userProfile.userId;
vm.username = userProfile.handle;
vm.toggleSkill = toggleSkill;
vm.tracks = {};
vm.mySkills = [];
vm.disableDoneButton = false;
+ vm.showCommunity = false;
+ vm.loadingCommunities = false;
+ vm.communities = {};
+ vm.isPageDirty = isPageDirty;
///////
activate();
+ /**
+ * Activates the controller.
+ */
function activate() {
- $log.debug("init")
+ $log.debug("init");
+ initCommunities();
+ checkCommunityStatus();
}
+ /**
+ * Verfies if the page state has been modified by the user in any way.
+ */
+ function isPageDirty() {
+ return isTracksDirty() || isCommunitiesDirty();
+ }
+
+ /**
+ * Verfies if the tracks section state has been modified by the user in any way.
+ */
+ function isTracksDirty() {
+ return vm.tracks.DESIGN || vm.tracks.DEVELOP || vm.tracks.DATA_SCIENCE;
+ }
+
+ /**
+ * Verfies if the communities section state has been modified by the user in any way.
+ */
+ function isCommunitiesDirty() {
+ var community = _.findWhere(vm.communities, {dirty: true});
+ return !!community;
+ }
+
+ /**
+ * Initializes the communities to show in the communities section.
+ */
+ function initCommunities() {
+ vm.communities['ios'] = { displayName: 'iOS', programId: vm.IOS_PROGRAM_ID, status: false, dirty: false, display: true};
+ _addWatchToCommunity(vm.communities['ios']);
+ }
+
+ /**
+ * Helper method to add watch to given object.
+ */
+ function _addWatchToCommunity(community) {
+ community.unregister = $scope.$watch(
+ function() { return community; },
+ function(newValue, oldValue) {
+ if (oldValue && newValue.status !== oldValue.status) {
+ newValue.dirty = oldValue.dirty ? false : true;
+ }
+ },
+ true
+ );
+ }
+
+ /**
+ * Checks registration status of each community and updates the state of each community.
+ */
+ function checkCommunityStatus() {
+ var promises = [];
+ for (var name in vm.communities) {
+ var community = vm.communities[name];
+ promises.push(MemberCertService.getMemberRegistration(vm.userId, community.programId));
+ }
+ vm.loadingCommunities = true;
+ $q.all(promises).then(function(responses) {
+ vm.loadingCommunities = false;
+ responses.forEach(function(program) {
+ if (program) {
+ var community = _.findWhere(vm.communities, {programId: program.eventId});
+ if (community) {
+ // set display false to avoid showing already enabled/registered program
+ // we expect display property to be modified after first load of the page
+ community.display = false;
+ community.status = true;
+ if (community.unregister){
+ community.unregister();
+ _addWatchToCommunity(community);
+ }
+ }
+ }
+ });
+ // if there exists at least 1 community which can be displayed, set showCommunity flag to true
+ var community = _.findWhere(vm.communities, {display: true});
+ if (community) {
+ vm.showCommunity = true;
+ }
+ })
+ .catch(function(error) {
+ vm.loadingCommunities = false;
+ });
+ }
+
+ /**
+ * Toggles the given skill for the user. If it is not added, adds it and if already added, removes it.
+ */
function toggleSkill(tagId) {
var _idx = vm.mySkills.indexOf(tagId.toString());
if (_idx > -1) {
@@ -34,50 +131,56 @@
}
}
+ /**
+ * Persists the user's altered information.
+ */
function submitSkills() {
vm.saving = true;
- // save tracks
- userProfile.tracks = _.reduce(vm.tracks, function(result, isInterested, trackName) {
- if (isInterested) {
- result.push(trackName);
- }
- return result;
- }, []);
- userProfile.save().then(function(data) {
- if (vm.mySkills.length > 0) {
- // save skills
- var data = {};
- for (var i = 0; i < vm.mySkills.length; i++) {
- data[vm.mySkills[i]] = {
- hidden: false
- };
+ var promises = [];
+ if (isTracksDirty()) {
+ // save tracks
+ userProfile.tracks = _.reduce(vm.tracks, function(result, isInterested, trackName) {
+ if (isInterested) {
+ result.push(trackName);
+ }
+ return result;
+ }, []);
+ promises.push(userProfile.save());
+ }
+ if (vm.mySkills.length > 0) {
+ // save skills
+ var data = {};
+ for (var i = 0; i < vm.mySkills.length; i++) {
+ data[vm.mySkills[i]] = {
+ hidden: false
+ };
+ }
+ promises.push(ProfileService.updateUserSkills(vm.username, data));
+ }
+ $log.debug('isCommunitiesDirty: ' + isCommunitiesDirty());
+ if (isCommunitiesDirty()) {
+ for(var communityName in vm.communities) {
+ var community = vm.communities[communityName];
+ if (community.dirty === true) {
+ if (community.status === true) {
+ promises.push(MemberCertService.registerMember(vm.userId, community.programId));
}
- ProfileService.updateUserSkills(vm.username, data)
- .then(function(resp) {
- vm.saving = false;
- toaster.pop('success', "Success!", "Your skills have been updated.");
- vm.disableDoneButton = true;
- $state.go('dashboard');
- })
- .catch(function(data) {
- vm.saving = false;
- toaster.pop('error', "Whoops", "Something went wrong. Please try again later.");
- })
- } else {
- vm.saving = false;
- toaster.pop('success', "Success!", "Your skills have been updated.");
- vm.disableDoneButton = true;
- $state.go('dashboard');
}
+ }
+ }
- })
- .catch(function(resp) {
- vm.saving = false;
- toaster.pop('error', "Whoops", "Something went wrong. Please try again later.");
- })
-
+ $q.all(promises).then(function(responses) {
+ vm.saving = false;
+ toaster.pop('success', "Success!", "Your skills have been updated.");
+ vm.disableDoneButton = true;
+ $state.go('dashboard');
+ })
+ .catch(function(resp) {
+ vm.saving = false;
+ toaster.pop('error', "Whoops!", "Something went wrong. Please try again later.");
+ });
}
}
})();
diff --git a/app/skill-picker/skill-picker.jade b/app/skill-picker/skill-picker.jade
index 63f450525..812f79bc5 100644
--- a/app/skill-picker/skill-picker.jade
+++ b/app/skill-picker/skill-picker.jade
@@ -3,6 +3,25 @@
p.instruction Hi {{vm.username}}! Your account is now active. To help other members get to know you, select the tracks in which you're interested, and specify some of your skills. You can edit this information later on your Profile.
+ .communities(ng-show="!vm.loadingCommunities && vm.showCommunity")
+ .communities__title Communities
+ .communities__description Topcoder regularly establishes exclusive communities to help members develop expertise and earn money in particular technologies. Join a featured community to be notified of events and challenges.
+ .communities__list
+ .community(ng-repeat="(communityKey, community) in vm.communities", ng-class="{'community--disabled': !community.status}", ng-if="community.display")
+ .community__details
+ .community__icon(ng-class="{'community__icon--disabled': !community.status}")
+ img(ng-if="communityKey == 'ios' && community.status", src="/images/ico-ios-community.svg")
+ img(ng-if="communityKey == 'ios' && !community.status", src="/images/ico-ios-community-grey.svg")
+
+ .community__text
+ span.community__title(class="{{!community.status && 'disabled'}}") {{community.displayName}}
+ .community__description
+ span(ng-if="communityKey == 'ios'") Mobile app design and development for iOS, with Swift emphasis
+
+ onoff-switch(model="community.status", unique-id="'community-' + communityKey")
+
+
+
.tracks-container
.title tracks
.description Topcoder's three categories of challenges… please pick at least one based on your skills and interests.
@@ -51,4 +70,4 @@
type="button",
tc-busy-button, tc-busy-when="vm.saving",
ng-click="vm.submitSkills()",
- ng-disabled="vm.disableDoneButton || (!vm.tracks.DESIGN && !vm.tracks.DEVELOP && !vm.tracks.DATA_SCIENCE)") Done
+ ng-disabled="vm.disableDoneButton || !vm.isPageDirty()") Done
diff --git a/app/skill-picker/skill-picker.spec.js b/app/skill-picker/skill-picker.spec.js
index f4e938b68..cad2670e8 100644
--- a/app/skill-picker/skill-picker.spec.js
+++ b/app/skill-picker/skill-picker.spec.js
@@ -1,19 +1,244 @@
/* jshint -W117, -W030 */
describe('Skill Picker Controller', function() {
var vm;
+ var toasterSvc, memberCertService, profileService, state;
+ var mockProfile = mockData.getMockProfile();
- // beforeEach(function() {
- // bard.appModule('tc.skill-picker');
- // bard.inject(this, '$controller', '$rootScope', '$q', 'userProfile');
+ beforeEach(function() {
+ bard.appModule('tc.skill-picker');
+ bard.inject(this, '$controller', '$rootScope', '$q', 'MemberCertService', 'ProfileService', 'toaster', 'CONSTANTS');
- // vm = $controller('SkillPickerController', {
- // });
- // });
+ memberCertService = MemberCertService;
+ profileService = ProfileService;
+ var swiftProgramId = CONSTANTS.SWIFT_PROGRAM_ID;
- // bard.verifyNoOutstandingHttpRequests();
+ // mock member cert api
+ sinon.stub(memberCertService, 'getMemberRegistration', function(userId, programId) {
+ var deferred = $q.defer();
+ if (programId === swiftProgramId) {
+ var resp = {eventId: swiftProgramId, userId: 12345};
+ deferred.resolve(resp);
+ } else {
+ deferred.resolve();
+ }
+ return deferred.promise;
+ });
+
+ sinon.stub(memberCertService, 'registerMember', function(userId, programId) {
+ var deferred = $q.defer();
+ var resp = {eventId: programId, userId: 12345};
+ if (programId === 100) {
+ deferred.reject();
+ } else {
+ deferred.resolve(resp);
+ }
+ return deferred.promise;
+ });
- // it('should be created successfully', function() {
- // expect(vm).to.exist;
- // });
+ // adds spy method to mocked profile object for saving the profile
+ mockProfile.save = sinon.spy();
+
+ // mock profile service
+ sinon.stub(profileService, 'updateUserSkills', function(username, skills) {
+ var deferred = $q.defer();
+ var resp = {};
+ if (skills[412]) {
+ deferred.reject();
+ } else {
+ deferred.resolve(resp);
+ }
+ return deferred.promise;
+ });
+
+ // mocks the toaster service
+ toasterSvc = toaster;
+ bard.mockService(toaster, {
+ pop: $q.when(true),
+ default: $q.when(true)
+ });
+
+ // mocked $state object
+ state = { go: sinon.spy()};
+
+ var scope = $rootScope.$new();
+ vm = $controller('SkillPickerController', {
+ $scope: scope,
+ userProfile: mockProfile,
+ featuredSkills: [],
+ $state: state
+
+ });
+ $rootScope.$digest();
+ });
+
+ bard.verifyNoOutstandingHttpRequests();
+
+ it('should be created successfully', function() {
+ expect(vm).to.exist;
+ expect(vm.showCommunity).to.exist.to.false;
+ });
+
+ it('should have empty tracks object ', function() {
+ expect(vm.tracks).to.exist;
+ expect(vm.tracks.DESIGN).not.to.exist;
+ expect(vm.tracks.DEVELOP).not.to.exist;
+ expect(vm.tracks.DATA_SCIENCE).not.to.exist;
+ });
+
+ it('should have correct userIdentity ', function() {
+ expect(vm.userId).to.exist.to.equal(mockProfile.userId);
+ expect(vm.username).to.exist.to.equal(mockProfile.handle);
+ });
+
+ it('should not have page dirty ', function() {
+ var dirty = vm.isPageDirty();
+ expect(dirty).to.equal(false);
+ });
+
+ it('should be created successfully with showCommunity being true', function() {
+ // backup original swift program id, to restore after test execution
+ var origSwiftProgId = CONSTANTS.SWIFT_PROGRAM_ID;
+ // update swift program id to something which says, member is not registered
+ CONSTANTS.SWIFT_PROGRAM_ID = 3446;
+ vm = $controller('SkillPickerController', {
+ $scope: $rootScope.$new(),
+ userProfile: mockProfile,
+ featuredSkills: []
+ });
+ $rootScope.$digest();
+ expect(vm).to.exist;
+ // showCommunity flag should be set to true because we have at least one unregistered community
+ expect(vm.showCommunity).to.exist.to.true;
+ // restores the original swift program id
+ CONSTANTS.SWIFT_PROGRAM_ID = origSwiftProgId;
+ });
+
+ it('should add skill ', function() {
+ vm.toggleSkill(409);
+ expect(vm.mySkills).to.exist.have.length(1);
+ });
+
+ it('should remove added skill ', function() {
+ vm.toggleSkill(409);
+ // should add the skill
+ expect(vm.mySkills).to.exist.have.length(1);
+ // next call to toggleSkill with same argument, should remove that skill
+ vm.toggleSkill(409);
+ // should remove the skill
+ expect(vm.mySkills).to.exist.have.length(0);
+ });
+
+ it('should not make any update call without any change ', function() {
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).not.to.be.called;
+ // we should still go to dashboard if the function is called,
+ // call to the function is controlled by disabling the button
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ it('should update tracks for the member ', function() {
+ vm.tracks.DEVELOP = true;
+ vm.tracks.DESIGN = false;
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).to.be.calledOnce;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).not.to.be.called;
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ it('should show error popup for updating tracks ', function() {
+ vm.tracks.DEVELOP = true;
+ // stubs the save method to reject the promise
+ mockProfile.save = function() {};
+ sinon.stub(mockProfile, 'save', function() {
+ var deferred = $q.defer();
+ deferred.reject();
+ return deferred.promise;
+ });
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).to.be.calledOnce;
+ expect(toasterSvc.pop).to.have.been.calledWith('error', "Whoops!", sinon.match('wrong')).calledOnce;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).not.to.be.called;
+ expect(state.go).not.to.be.called;
+ });
+
+ it('should update skills for the member ', function() {
+ vm.mySkills = [409, 410];
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).to.be.calledOnce;
+ expect(memberCertService.registerMember).not.to.be.called;
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ it('should show error popup for error in updating skills ', function() {
+ vm.mySkills = [409, 410, 412];
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).to.be.calledOnce;
+ expect(toasterSvc.pop).to.have.been.calledWith('error', "Whoops!", sinon.match('wrong')).calledOnce;
+ expect(memberCertService.registerMember).not.to.be.called;
+ expect(state.go).not.to.be.called;
+ });
+
+ it('should update communities for the member ', function() {
+ vm.communities['ios'].status = true;
+ vm.communities['ios'].dirty = true;
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).to.be.calledOnce;
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ // we may need to update this test case when we want to call unregister endpoint
+ it('should NOT update communities for the member for disabled community ', function() {
+ vm.communities['ios'].status = false;
+ vm.communities['ios'].dirty = true;
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).not.to.be.called;
+ // we should still go to dashboard if the function is called,
+ // call to the function is controlled by disabling the button
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ it('should NOT update communities for the member for enabled but non dirty community ', function() {
+ // TODO need one more community, with dirty true, to test out the non dirty community update
+ vm.communities['ios'].status = true;
+ vm.communities['ios'].dirty = false;
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).not.to.be.called;
+ // we should still go to dashboard if the function is called,
+ // call to the function is controlled by disabling the button
+ expect(state.go).to.have.been.calledWith('dashboard').calledOnce;
+ });
+
+ it('should show error popup for error in updating communities ', function() {
+ vm.communities['ios'].programId = 100;// to cause rejction of the promise
+ vm.communities['ios'].status = true;
+ vm.communities['ios'].dirty = true;
+ vm.submitSkills();
+ $rootScope.$digest();
+ expect(mockProfile.save).not.to.be.called;
+ expect(profileService.updateUserSkills).not.to.be.called;
+ expect(memberCertService.registerMember).to.be.calledOnce;
+ expect(toasterSvc.pop).to.have.been.calledWith('error', "Whoops!", sinon.match('wrong')).calledOnce;
+ expect(state.go).not.to.be.called;
+ });
});
diff --git a/app/topcoder.constants.js b/app/topcoder.constants.js
index fddccd379..037784533 100644
--- a/app/topcoder.constants.js
+++ b/app/topcoder.constants.js
@@ -32,7 +32,8 @@ angular.module("CONSTANTS", [])
"BUSY_PROGRESS_MESSAGE": "Processing..",
"REGISTRATION": "REGISTRATION",
"CODING": "CODING",
- "REGISTERED": "REGISTERED"
+ "REGISTERED": "REGISTERED",
+ "SUBMISSION_TYPE_CONTEST": "Contest Submission"
})
;
\ No newline at end of file
diff --git a/assets/css/community/members.scss b/assets/css/community/members.scss
index a8475b936..95d685da3 100644
--- a/assets/css/community/members.scss
+++ b/assets/css/community/members.scss
@@ -181,7 +181,7 @@
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1);
text-align: center;
background: $white;
- .avatar-wrapper {
+ .avatar-wrapper img {
margin: 0 auto;
border-radius: 100px;
border: 1px solid $gray-light;
@@ -209,6 +209,10 @@
.user-more {
text-transform: uppercase;
}
+ .user-name {
+ color: black;
+ display: block;
+ }
.user-desc {
color: $gray-dark-alt;
min-height: 54px;
diff --git a/assets/css/directives/design-challenge-user-place.scss b/assets/css/directives/design-challenge-user-place.scss
index bcba91355..133a86446 100644
--- a/assets/css/directives/design-challenge-user-place.scss
+++ b/assets/css/directives/design-challenge-user-place.scss
@@ -11,7 +11,7 @@ design-challenge-user-place {
.place {
&.completed, &.passed, &.didnt {
margin-bottom: -36px;
- margin-top: 40px;
+ margin-top: 52px;
}
margin-bottom: 8px;
margin-top: 8px;
diff --git a/assets/css/directives/srm-tile.scss b/assets/css/directives/srm-tile.scss
index ca088326a..eb361c3ce 100644
--- a/assets/css/directives/srm-tile.scss
+++ b/assets/css/directives/srm-tile.scss
@@ -2,6 +2,9 @@
// common styles for both list and tile view
.srm {
+ .noclick {
+ cursor: default;
+ }
.phase-status {
.registered {
position: relative;
diff --git a/assets/css/skill-picker/skill-picker.scss b/assets/css/skill-picker/skill-picker.scss
index 4b18ed13e..6432cbb22 100644
--- a/assets/css/skill-picker/skill-picker.scss
+++ b/assets/css/skill-picker/skill-picker.scss
@@ -30,6 +30,67 @@
color: $accent-gray;
}
+ .communities {
+ width: 100%;
+ margin-bottom: 30px;
+ .communities__title {
+ @include sofia-pro-regular;
+ text-transform: uppercase;
+ font-size: 18px;
+ padding-bottom: 10px;
+ }
+ .communities__description {
+ @include font-with-weight('Merriweather Sans');
+ color: #A3A3AE;
+ font-size: 13px;
+ line-height: 18px;
+ }
+ .communities__list {
+ margin-top: 10px;
+ .community {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ padding: 15px 0;
+ border-bottom: 1px solid $gray-light;
+
+ &.disabled {
+ background-color: $gray-lightest;
+ }
+
+ .community__details {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding-left: 10px;
+ }
+
+ .community__text {
+ margin-left: 10px;
+ }
+
+ .community__title {
+ font-size: 16px;
+ line-height: 28px;
+ @include font-with-weight('Sofia Pro', 500);
+ transition: .1s color;
+
+ &.disabled {
+ color: $gray-dark;
+ }
+ }
+
+ .community__description {
+ @include font-with-weight('Merriweather Sans', 400);
+ font-size: 13px;
+ margin-top: 4px;
+ color: $accent-gray;
+ }
+ }
+ }
+ }
+
.tracks-container {
width: 100%;
>.title {
diff --git a/assets/images/ico-ios-community-grey.svg b/assets/images/ico-ios-community-grey.svg
new file mode 100755
index 000000000..7dfad082d
--- /dev/null
+++ b/assets/images/ico-ios-community-grey.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/assets/images/ico-ios-community.svg b/assets/images/ico-ios-community.svg
new file mode 100755
index 000000000..3fa0795ca
--- /dev/null
+++ b/assets/images/ico-ios-community.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/assets/images/skills/id-109.svg b/assets/images/skills/id-109.svg
old mode 100644
new mode 100755
index df7695e7a..c5bd3faa3
--- a/assets/images/skills/id-109.svg
+++ b/assets/images/skills/id-109.svg
@@ -1,17 +1,13 @@
-