From d444d0e2b05da6227ff555edaa926fb5f616a5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 22 Sep 2015 10:43:17 +0200 Subject: [PATCH 1/9] Update gulp sass --- app/modules/external-apps/external-app.controller.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/modules/external-apps/external-app.controller.coffee b/app/modules/external-apps/external-app.controller.coffee index 21561f9a..6c963753 100644 --- a/app/modules/external-apps/external-app.controller.coffee +++ b/app/modules/external-apps/external-app.controller.coffee @@ -12,8 +12,8 @@ class ExternalAppController extends taiga.Controller "tgLoader" ] - constructor: (@routeParams, @externalAppsService, @window, @currentUserService, @location, @navUrls, - @xhrError, @loader) -> + constructor: (@routeParams, @externalAppsService, @window, @currentUserService, @location, + @navUrls, @xhrError, @loader) -> @loader.start(false) @._applicationId = @routeParams.application @._state = @routeParams.state From 29891b641d2a55397b3621e8b2279d3eda7e83bb Mon Sep 17 00:00:00 2001 From: Juanfran Date: Fri, 2 Oct 2015 07:59:33 +0200 Subject: [PATCH 2/9] init joyride --- app/coffee/app.coffee | 11 +- app/index.jade | 2 + app/locales/locale-en.json | 56 +++++++ .../joy-ride/joy-ride.directive.coffee | 45 ++++++ .../joy-ride/joy-ride.service.coffee | 142 ++++++++++++++++++ .../joy-ride/joy-ride.service.spec.coffee | 47 ++++++ gulpfile.js | 6 +- 7 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 app/modules/components/joy-ride/joy-ride.directive.coffee create mode 100644 app/modules/components/joy-ride/joy-ride.service.coffee create mode 100644 app/modules/components/joy-ride/joy-ride.service.spec.coffee diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 29c5ed73..0ae11c6a 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -68,7 +68,9 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven }, loader: true, title: "HOME.PAGE_TITLE", + loader: true, description: "HOME.PAGE_DESCRIPTION", + joyride: "dashboard" } ) @@ -109,7 +111,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven { templateUrl: "backlog/backlog.html", loader: true, - section: "backlog" + section: "backlog", + joyride: "backlog" } ) @@ -117,7 +120,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven { templateUrl: "kanban/kanban.html", loader: true, - section: "kanban" + section: "kanban", + joyride: "kanban" } ) @@ -703,6 +707,7 @@ module.run([ "tgAppMetaService", "tgProjectService", "tgLoader", - "tgNavigationBarService" + "tgNavigationBarService", + "$route", init ]) diff --git a/app/index.jade b/app/index.jade index e50edda1..a9994cef 100644 --- a/app/index.jade +++ b/app/index.jade @@ -37,6 +37,8 @@ html(lang="en") include partials/includes/components/notification-message + div(tg-joy-ride) + script(src="/js/libs.js?v=#{v}") script(src="/js/templates.js?v=#{v}") script(src="/js/app-loader.js?v=#{v}") diff --git a/app/locales/locale-en.json b/app/locales/locale-en.json index c1a97761..7c51abee 100644 --- a/app/locales/locale-en.json +++ b/app/locales/locale-en.json @@ -1284,5 +1284,61 @@ "LOGIN_WITH_ANOTHER_USER": "Login with another user", "AUTHORIZE_APP": "Authorize app", "CANCEL": "Cancel" + }, + "JOYRIDE": { + "DASHBOARD": { + "STEP1": { + "TITLE": "Your project", + "TEXT": "Welcome! Here you will find the projects you are involved on. We have left you sample project templates to help you discover the power of Taiga." + }, + "STEP2": { + "TITLE": "Working on", + "TEXT": "Here you will find the User Stories, Tasks and Issues in which you are working on." + }, + "STEP3": { + "TITLE": "Watching", + "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT2": "You are already working with Taiga ;)" + }, + "STEP4": { + "TITLE": "Let’s start", + "TEXT1": "You can start by creating your first Taiga project or taking a look at the sample templates that we have left for you.", + "TEXT2": "Good luck!" + } + }, + "BACKLOG": { + "STEP1": { + "TITLE": "Project summary", + "TEXT1": "Here you will see the state of your project.", + "TEXT2": "You can change every kind of project settings through the admin." + }, + "STEP2": { + "TITLE": "Product backlog", + "TEXT": "The backlog is the list of requirements (User Stories) for the project. Here is where you will plan your sprints." + }, + "STEP3": { + "TITLE": "Sprints", + "TEXT": "Sprints are short periods of time (usually 2 weeks) during which specific work has to be completed and delivered." + }, + "STEP4": { + "TITLE": "User Stories", + "TEXT": "Those are the requirements at high level. You can add them to the backlog and drag them to the sprint in which it should be delivered." + } + }, + "KANBAN": { + "STEP1": { + "TITLE": "Customize your workflow", + "TEXT": "Set up the columns you need to map your workflow statuses through the admin." + }, + "STEP2": { + "TITLE": "User Stories & Tasks", + "TEXT": "User Stories are the requirements at high level. You can drag them to different columns." + }, + "STEP3": { + "TITLE": "Adding User Stories", + "TEXT1": "You may want to add a single User Story (add US icon) or a group of them (bulk icon)", + "TEXT2": "Good luck!" + } + } } } diff --git a/app/modules/components/joy-ride/joy-ride.directive.coffee b/app/modules/components/joy-ride/joy-ride.directive.coffee new file mode 100644 index 00000000..6755310e --- /dev/null +++ b/app/modules/components/joy-ride/joy-ride.directive.coffee @@ -0,0 +1,45 @@ +taiga = @.taiga + +JoyRideDirective = ($rootScope, currentUserService, joyRideService) -> + link = (scope, el, attrs, ctrl) -> + intro = introJs() + + #Todo: translate + intro.setOptions({ + exitOnEsc: false, + exitOnOverlayClick: false, + nextLabel: 'Next →', + prevLabel: '← Back', + skipLabel: 'Skip', + doneLabel: 'Done' + }) + + intro.oncomplete () -> + $('html,body').scrollTop(0) + + startIntro = (joyRideName) -> + intro.setOption('steps', joyRideService.get(joyRideName)) + intro.start(); + + $rootScope.$on '$routeChangeSuccess', (event, next) -> + return if !next.joyride || !currentUserService.isAuthenticated() + + if next.loader + un = $rootScope.$on 'loader:end', () -> + startIntro(next.joyride) + un() + else + startIntro(next.joyride) + + return { + scope: {}, + link: link + } + +JoyRideDirective.$inject = [ + "$rootScope", + "tgCurrentUserService", + "tgJoyRideService" +] + +angular.module("taigaComponents").directive("tgJoyRide", JoyRideDirective) diff --git a/app/modules/components/joy-ride/joy-ride.service.coffee b/app/modules/components/joy-ride/joy-ride.service.coffee new file mode 100644 index 00000000..1ad76541 --- /dev/null +++ b/app/modules/components/joy-ride/joy-ride.service.coffee @@ -0,0 +1,142 @@ +joyRides = { + dashboard: () -> + return [ + { + element: '.home-project-list', + position: 'left', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TITLE'), + text: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TEXT') + } + }, + { + element: '.working-on-title', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TITLE'), + text: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TEXT') + } + }, + { + element: '.watching-title', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP3.TITLE') + text: [ + @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT1'), + @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT2') + ] + } + }, + { + element: '.project-list .see-more-projects-btn', + position: 'button', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP4.TITLE') + text: [ + @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT1'), + @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT2') + ] + } + } + ] + + backlog: () -> + return [ + { + element: '.summary', + position: 'button', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP1.TITLE') + text: [ + @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT1'), + @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT2') + ] + } + }, + { + element: '.backlog-table', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP2.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP2.TEXT') + } + }, + { + element: '.sprints', + position: 'left', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP3.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP3.TEXT') + } + }, + { + element: '.new-us', + position: 'rigth', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP3.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP3.TEXT') + } + } + ] + + kanban: () -> + return [ + { + element: '.kanban-table-inner', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP1.TITLE') + text: @translate.instant('JOYRIDE.KANBAN.STEP1.TEXT') + } + }, + { + element: '.kanban-uses-box', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP2.TITLE') + text: @translate.instant('JOYRIDE.KANBAN.STEP2.TEXT') + } + }, + { + element: 'div[tg-kanban-squish-column] h2', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') + text: [ + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT1'), + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT2'), + ] + } + } + ] +} + + +class JoyRideService extends taiga.Service + @.$inject = [ + '$translate' + ] + + constructor: (@translate) -> + + get: (name) -> + joyRide = joyRides[name].call(this) + + return _.map joyRide, (item) -> + html = "" + + if item.joyride.title + html += "

#{item.joyride.title}

" + + if _.isArray(item.joyride.text) + _.forEach item.joyride.text, (text) -> + html += "

#{text}

" + else + html += "

#{item.joyride.text}

" + + item.intro = html + + return item + +angular.module("taigaComponents").service("tgJoyRideService", JoyRideService) diff --git a/app/modules/components/joy-ride/joy-ride.service.spec.coffee b/app/modules/components/joy-ride/joy-ride.service.spec.coffee new file mode 100644 index 00000000..eba61f97 --- /dev/null +++ b/app/modules/components/joy-ride/joy-ride.service.spec.coffee @@ -0,0 +1,47 @@ +describe "tgJoyRideService", -> + joyRideService = provide = null + mocks = {} + + _mockTranslate = () -> + mocks.translate = { + instant: sinon.stub() + } + + provide.value "$translate", mocks.translate + + _inject = (callback) -> + inject (_tgJoyRideService_) -> + joyRideService = _tgJoyRideService_ + callback() if callback + + _mocks = () -> + module ($provide) -> + provide = $provide + _mockTranslate() + return null + + _setup = -> + _mocks() + + beforeEach -> + module "taigaComponents" + _setup() + _inject() + + it "get joyride by category", () -> + example = { + element: '.home-project-list', + position: 'left', + joyride: { + title: 'test', + text: 'test' + }, + intro: '

test

test

' + } + + mocks.translate.instant.returns('test') + + joyRide = joyRideService.get('dashboard') + + expect(joyRide).to.have.length(4) + expect(joyRide[0]).to.be.eql(example) diff --git a/gulpfile.js b/gulpfile.js index 321bf478..eedf61f8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -58,7 +58,10 @@ paths.htmlPartials = [ paths.images = paths.app + "images/**/*"; paths.svg = paths.app + "svg/**/*"; -paths.css_vendor = paths.app + "styles/vendor/*.css"; +paths.css_vendor = [ + paths.app + "styles/vendor/*.css", + paths.vendor + "intro.js/introjs.css" +]; paths.locales = paths.app + "locales/**/*.json"; paths.sass = [ @@ -164,6 +167,7 @@ paths.libs = [ paths.vendor + "ngInfiniteScroll/build/ng-infinite-scroll.js", paths.vendor + "eventemitter2/lib/eventemitter2.js", paths.vendor + "immutable/dist/immutable.js", + paths.vendor + "intro.js/intro.js", paths.app + "js/jquery.ui.git-custom.js", paths.app + "js/jquery-ui.drag-multiple-custom.js", paths.app + "js/jquery.ui.touch-punch.min.js", From b098ef63f6326b8bc12421689200a3e44524715b Mon Sep 17 00:00:00 2001 From: Juanfran Date: Mon, 5 Oct 2015 12:33:17 +0200 Subject: [PATCH 3/9] save joyride in user storage --- .../joy-ride/joy-ride.directive.coffee | 22 +++++++--- app/modules/resources/resources.coffee | 1 + .../resources/user-resource.service.coffee | 41 +++++++++++++++++ .../services/current-user.service.coffee | 44 +++++++++++++++++-- .../services/current-user.service.spec.coffee | 44 +++++++++++++++++++ 5 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 app/modules/resources/user-resource.service.coffee diff --git a/app/modules/components/joy-ride/joy-ride.directive.coffee b/app/modules/components/joy-ride/joy-ride.directive.coffee index 6755310e..9464aad0 100644 --- a/app/modules/components/joy-ride/joy-ride.directive.coffee +++ b/app/modules/components/joy-ride/joy-ride.directive.coffee @@ -17,19 +17,31 @@ JoyRideDirective = ($rootScope, currentUserService, joyRideService) -> intro.oncomplete () -> $('html,body').scrollTop(0) - startIntro = (joyRideName) -> - intro.setOption('steps', joyRideService.get(joyRideName)) - intro.start(); + intro.onexit () -> + currentUserService.disableJoyRide() + + initJoyrRide = (next, config) -> + if !config[next.joyride] + return + + intro.setOption('steps', joyRideService.get(next.joyride)) + intro.start() $rootScope.$on '$routeChangeSuccess', (event, next) -> return if !next.joyride || !currentUserService.isAuthenticated() + intro.oncomplete () -> + currentUserService.disableJoyRide(next.joyride) + if next.loader un = $rootScope.$on 'loader:end', () -> - startIntro(next.joyride) + currentUserService.loadJoyRideConfig() + .then (config) -> initJoyrRide(next, config) + un() else - startIntro(next.joyride) + currentUserService.loadJoyRideConfig() + .then (config) -> initJoyrRide(next, config) return { scope: {}, diff --git a/app/modules/resources/resources.coffee b/app/modules/resources/resources.coffee index 8d73ce4d..077cb726 100644 --- a/app/modules/resources/resources.coffee +++ b/app/modules/resources/resources.coffee @@ -1,5 +1,6 @@ services = [ "tgProjectsResources", + "tgUserResources", "tgUsersResources", "tgUserstoriesResource", "tgTasksResource", diff --git a/app/modules/resources/user-resource.service.coffee b/app/modules/resources/user-resource.service.coffee new file mode 100644 index 00000000..d51d3498 --- /dev/null +++ b/app/modules/resources/user-resource.service.coffee @@ -0,0 +1,41 @@ +Resource = (urlsService, http, paginateResponseService) -> + service = {} + + service.getUserStorage = (key) -> + url = urlsService.resolve("user-storage") + + if key + url += '/' + key + + httpOptions = {} + + return http.get(url, {}).then (response) -> + return response.data.value + + service.setUserStorage = (key, value) -> + url = urlsService.resolve("user-storage") + '/' + key + + params = { + key: key, + value: value + } + + return http.put(url, params) + + service.createUserStorage = (key, value) -> + url = urlsService.resolve("user-storage") + + params = { + key: key, + value: value + } + + return http.post(url, params) + + return () -> + return {"user": service} + +Resource.$inject = ["$tgUrls", "$tgHttp"] + +module = angular.module("taigaResources2") +module.factory("tgUserResources", Resource) diff --git a/app/modules/services/current-user.service.coffee b/app/modules/services/current-user.service.coffee index a4301dae..21d7c217 100644 --- a/app/modules/services/current-user.service.coffee +++ b/app/modules/services/current-user.service.coffee @@ -5,13 +5,15 @@ groupBy = @.taiga.groupBy class CurrentUserService @.$inject = [ "tgProjectsService", - "$tgStorage" + "$tgStorage", + "tgResources" ] - constructor: (@projectsService, @storageService) -> + constructor: (@projectsService, @storageService, @rs) -> @._user = null @._projects = Immutable.Map() @._projectsById = Immutable.Map() + @._joyride = null taiga.defineImmutableProperty @, "projects", () => return @._projects taiga.defineImmutableProperty @, "projectsById", () => return @._projectsById @@ -55,7 +57,43 @@ class CurrentUserService return @.projects + disableJoyRide: (section) -> + if section + @._joyride[section] = false + else + @._joyride = { + backlog: false, + kanban: false, + dashboard: false + } + + @rs.user.setUserStorage('joyride', @._joyride) + + loadJoyRideConfig: () -> + return new Promise (resolve) => + if @._joyride != null + resolve(@._joyride) + return + + @rs.user.getUserStorage('joyride') + .then (config) => + @._joyride = config + resolve(@._joyride) + .catch () => + #joyride not defined + @._joyride = { + backlog: true, + kanban: true, + dashboard: true + } + + @rs.user.createUserStorage('joyride', @._joyride) + + resolve(@._joyride) + _loadUserInfo: () -> - return @.loadProjects() + return Promise.all([ + @.loadProjects() + ]) angular.module("taigaCommon").service("tgCurrentUserService", CurrentUserService) diff --git a/app/modules/services/current-user.service.spec.coffee b/app/modules/services/current-user.service.spec.coffee index e17e44e9..7a418ff0 100644 --- a/app/modules/services/current-user.service.spec.coffee +++ b/app/modules/services/current-user.service.spec.coffee @@ -17,6 +17,17 @@ describe "tgCurrentUserService", -> provide.value "tgProjectsService", mocks.projectsService + _mockResources = () -> + mocks.resources = { + user: { + setUserStorage: sinon.stub(), + getUserStorage: sinon.stub(), + createUserStorage: sinon.stub() + } + } + + provide.value "tgResources", mocks.resources + _inject = (callback) -> inject (_tgCurrentUserService_) -> currentUserService = _tgCurrentUserService_ @@ -27,6 +38,7 @@ describe "tgCurrentUserService", -> provide = $provide _mockTgStorage() _mockProjectsService() + _mockResources() return null @@ -105,3 +117,35 @@ describe "tgCurrentUserService", -> currentUserService.removeUser() expect(currentUserService._user).to.be.null + + it "disable joyride", () -> + currentUserService.disableJoyRide() + + expect(mocks.resources.user.setUserStorage).to.have.been.calledWith('joyride', { + backlog: false, + kanban: false, + dashboard: false + }); + + it "load joyride config", (done) -> + mocks.resources.user.getUserStorage.withArgs('joyride').promise().resolve(true) + + currentUserService.loadJoyRideConfig().then (config) -> + expect(config).to.be.true + + done() + + it "create default joyride config", (done) -> + mocks.resources.user.getUserStorage.withArgs('joyride').promise().reject() + + currentUserService.loadJoyRideConfig().then (config) -> + joyride = { + backlog: true, + kanban: true, + dashboard: true + } + + expect(mocks.resources.user.createUserStorage).to.have.been.calledWith('joyride', joyride) + expect(config).to.be.eql(joyride) + + done() From ca0f3a7001f56fb21a8d163314682ce94c33b2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Fri, 9 Oct 2015 11:48:21 +0200 Subject: [PATCH 4/9] WIP joyride override styles --- app/styles/modules/help/joyride.scss | 4 ++++ bower.json | 3 ++- gulpfile.js | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 app/styles/modules/help/joyride.scss diff --git a/app/styles/modules/help/joyride.scss b/app/styles/modules/help/joyride.scss new file mode 100644 index 00000000..5a9d7763 --- /dev/null +++ b/app/styles/modules/help/joyride.scss @@ -0,0 +1,4 @@ +.introjs-overlay { + background: radial-gradient(center, ellipse cover, rgba($whitish, .4) 0, rgba($whitish, .9) 100%); + background-color: $whitish; +} diff --git a/bower.json b/bower.json index 50937eb7..3bbd02bf 100644 --- a/bower.json +++ b/bower.json @@ -81,7 +81,8 @@ "ngInfiniteScroll": "1.2.1", "eventemitter2": "~0.4.14", "immutable": "~3.7.2", - "bluebird": "~2.10.2" + "bluebird": "~2.10.2", + "intro.js": "~1.1.1" }, "resolutions": { "lodash": "~2.4.2", diff --git a/gulpfile.js b/gulpfile.js index eedf61f8..dbd0e4d3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -248,7 +248,7 @@ gulp.task("scss-lint", [], function() { }; } }))) - .pipe(gulpif(fail, scsslint.failReporter())) + .pipe(gulpif(fail, scsslint.failReporter())); }); gulp.task("clear-sass-cache", function() { @@ -315,7 +315,7 @@ gulp.task("main-css", function() { return gulp.src(_paths) .pipe(concat("theme-" + themes.current.name + ".css")) .pipe(gulpif(isDeploy, minifyCSS({noAdvanced: true}))) - .pipe(gulp.dest(paths.dist + "styles/")) + .pipe(gulp.dest(paths.dist + "styles/")); }); var compileThemes = function (cb) { @@ -326,7 +326,7 @@ var compileThemes = function (cb) { ["app-css", "vendor-css"], "main-css", function() { - themes.next() + themes.next(); if (themes.current) { compileThemes(cb); From d55e4468af9164361463151159917f04059535a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Tue, 13 Oct 2015 15:03:27 +0200 Subject: [PATCH 5/9] Joyrides --- .../joy-ride/joy-ride.directive.coffee | 1 + .../joy-ride/joy-ride.service.coffee | 20 +++---- app/modules/home/working-on/working-on.jade | 26 +++++---- app/styles/modules/help/joyride.scss | 56 ++++++++++++++++++- 4 files changed, 80 insertions(+), 23 deletions(-) diff --git a/app/modules/components/joy-ride/joy-ride.directive.coffee b/app/modules/components/joy-ride/joy-ride.directive.coffee index 9464aad0..61a7c794 100644 --- a/app/modules/components/joy-ride/joy-ride.directive.coffee +++ b/app/modules/components/joy-ride/joy-ride.directive.coffee @@ -8,6 +8,7 @@ JoyRideDirective = ($rootScope, currentUserService, joyRideService) -> intro.setOptions({ exitOnEsc: false, exitOnOverlayClick: false, + showStepNumbers: false, nextLabel: 'Next →', prevLabel: '← Back', skipLabel: 'Skip', diff --git a/app/modules/components/joy-ride/joy-ride.service.coffee b/app/modules/components/joy-ride/joy-ride.service.coffee index 1ad76541..68258123 100644 --- a/app/modules/components/joy-ride/joy-ride.service.coffee +++ b/app/modules/components/joy-ride/joy-ride.service.coffee @@ -10,7 +10,7 @@ joyRides = { } }, { - element: '.working-on-title', + element: '.working-on-container', position: 'right', joyride: { title: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TITLE'), @@ -18,7 +18,7 @@ joyRides = { } }, { - element: '.watching-title', + element: '.watching-container', position: 'right', joyride: { title: @translate.instant('JOYRIDE.DASHBOARD.STEP3.TITLE') @@ -30,7 +30,7 @@ joyRides = { }, { element: '.project-list .see-more-projects-btn', - position: 'button', + position: 'bottom', joyride: { title: @translate.instant('JOYRIDE.DASHBOARD.STEP4.TITLE') text: [ @@ -45,7 +45,7 @@ joyRides = { return [ { element: '.summary', - position: 'button', + position: 'bottom', joyride: { title: @translate.instant('JOYRIDE.BACKLOG.STEP1.TITLE') text: [ @@ -55,8 +55,8 @@ joyRides = { } }, { - element: '.backlog-table', - position: 'right', + element: '.backlog-table-empty', + position: 'bottom', joyride: { title: @translate.instant('JOYRIDE.BACKLOG.STEP2.TITLE') text: @translate.instant('JOYRIDE.BACKLOG.STEP2.TEXT') @@ -74,8 +74,8 @@ joyRides = { element: '.new-us', position: 'rigth', joyride: { - title: @translate.instant('JOYRIDE.BACKLOG.STEP3.TITLE') - text: @translate.instant('JOYRIDE.BACKLOG.STEP3.TEXT') + title: @translate.instant('JOYRIDE.BACKLOG.STEP4.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP4.TEXT') } } ] @@ -91,7 +91,7 @@ joyRides = { } }, { - element: '.kanban-uses-box', + element: '.card-placeholder', position: 'right', joyride: { title: @translate.instant('JOYRIDE.KANBAN.STEP2.TITLE') @@ -99,7 +99,7 @@ joyRides = { } }, { - element: 'div[tg-kanban-squish-column] h2', + element: '.icon-plus', position: 'bottom', joyride: { title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') diff --git a/app/modules/home/working-on/working-on.jade b/app/modules/home/working-on/working-on.jade index 4777e130..f12edae1 100644 --- a/app/modules/home/working-on/working-on.jade +++ b/app/modules/home/working-on/working-on.jade @@ -1,17 +1,19 @@ -div.title-bar.working-on-title(translate="HOME.WORKING_ON_SECTION") +section.working-on-container + .title-bar.working-on-title(translate="HOME.WORKING_ON_SECTION") -section.working-on(ng-show="vm.assignedTo.size") - div.duty-single(tg-duty="duty", tg-repeat="duty in vm.assignedTo", ng-class="{blocked: duty.is_blocked}") + .working-on(ng-show="vm.assignedTo.size") + .duty-single(tg-duty="duty", tg-repeat="duty in vm.assignedTo", ng-class="{blocked: duty.is_blocked}") -section.working-on-empty(ng-show="!vm.assignedTo.size") - p(translate="HOME.EMPTY_WORKING_ON") - include empty.jade + .working-on-empty(ng-show="!vm.assignedTo.size") + p(translate="HOME.EMPTY_WORKING_ON") + include empty.jade -div.title-bar.watching-title(translate="HOME.WATCHING_SECTION") +section.watching-container + .title-bar.watching-title(translate="HOME.WATCHING_SECTION") -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}") + .watching(ng-show="vm.watching.size") + .duty-single(tg-duty="duty", tg-repeat="duty in vm.watching", ng-class="{blocked: duty.is_blocked}") -section.watching-empty(ng-show="!vm.watching.size") - p(translate="HOME.EMPTY_WATCHING") - include empty.jade + .watching-empty(ng-show="!vm.watching.size") + p(translate="HOME.EMPTY_WATCHING") + include empty.jade diff --git a/app/styles/modules/help/joyride.scss b/app/styles/modules/help/joyride.scss index 5a9d7763..c9938f01 100644 --- a/app/styles/modules/help/joyride.scss +++ b/app/styles/modules/help/joyride.scss @@ -1,4 +1,58 @@ +// scss-lint:disable SelectorFormat, QualifyingElement + .introjs-overlay { - background: radial-gradient(center, ellipse cover, rgba($whitish, .4) 0, rgba($whitish, .9) 100%); + background: radial-gradient(center, ellipse cover, rgba($white, .2) 0, rgba($whitish, .2) 100%); background-color: $whitish; } +.introjs-helperLayer { + border: 1px solid rgba($primary-light, .8); +} + +.introjs-helperLayer, +.introjs-tooltip { + box-shadow: 0 1px 8px rgba($grayer, .2); +} + +.introjs-tooltip { + h3 { + @extend %large; + margin-bottom: .5rem; + } + p { + @extend %light; + line-height: 1.4; + margin-bottom: 0; + } +} + +.introjs-bullets { + ul { + li { + a.active { + background: $primary-light; + } + } + } +} + +.introjs-button { + background-color: $primary; + background-image: none; + border: 0; + border-radius: 0; + color: $white; + margin-top: 10px; + padding: .3rem .8rem; + text-shadow: none; + &:focus, + &:hover { + background: none; + background-color: $primary-light; + color: $white; + } + &.introjs-disabled { + background: $whitish; + background-color: none; + color: $white; + } +} From a5ff8517cedfd36d906f59b546bf1e0a1873ca98 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Fri, 16 Oct 2015 10:56:24 +0200 Subject: [PATCH 6/9] disable joyride interaction --- app/modules/components/joy-ride/joy-ride.directive.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/modules/components/joy-ride/joy-ride.directive.coffee b/app/modules/components/joy-ride/joy-ride.directive.coffee index 61a7c794..f2d54a8c 100644 --- a/app/modules/components/joy-ride/joy-ride.directive.coffee +++ b/app/modules/components/joy-ride/joy-ride.directive.coffee @@ -12,7 +12,8 @@ JoyRideDirective = ($rootScope, currentUserService, joyRideService) -> nextLabel: 'Next →', prevLabel: '← Back', skipLabel: 'Skip', - doneLabel: 'Done' + doneLabel: 'Done', + disableInteraction: true }) intro.oncomplete () -> From ef8053ddd6183f1693bd7b6463ebeda4ea47738d Mon Sep 17 00:00:00 2001 From: Juanfran Date: Fri, 16 Oct 2015 10:56:40 +0200 Subject: [PATCH 7/9] fix dashboard projects selector --- app/modules/components/joy-ride/joy-ride.service.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/modules/components/joy-ride/joy-ride.service.coffee b/app/modules/components/joy-ride/joy-ride.service.coffee index 68258123..0a5e4d25 100644 --- a/app/modules/components/joy-ride/joy-ride.service.coffee +++ b/app/modules/components/joy-ride/joy-ride.service.coffee @@ -2,7 +2,7 @@ joyRides = { dashboard: () -> return [ { - element: '.home-project-list', + element: '.project-list > section:not(.ng-hide)', position: 'left', joyride: { title: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TITLE'), From 50db211c86a5112122409b76020e83b9db5d9096 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Mon, 19 Oct 2015 08:16:06 +0200 Subject: [PATCH 8/9] conditional backlog joyride step if the user has perms --- .../joy-ride/joy-ride.service.coffee | 240 +++++++++--------- .../joy-ride/joy-ride.service.spec.coffee | 12 +- .../services/check-permissions.service.coffee | 13 + .../check-permissions.service.spec.coffee | 44 ++++ 4 files changed, 191 insertions(+), 118 deletions(-) create mode 100644 app/modules/services/check-permissions.service.coffee create mode 100644 app/modules/services/check-permissions.service.spec.coffee diff --git a/app/modules/components/joy-ride/joy-ride.service.coffee b/app/modules/components/joy-ride/joy-ride.service.coffee index 0a5e4d25..39e16c70 100644 --- a/app/modules/components/joy-ride/joy-ride.service.coffee +++ b/app/modules/components/joy-ride/joy-ride.service.coffee @@ -1,126 +1,132 @@ -joyRides = { - dashboard: () -> - return [ - { - element: '.project-list > section:not(.ng-hide)', - position: 'left', - joyride: { - title: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TITLE'), - text: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TEXT') - } - }, - { - element: '.working-on-container', - position: 'right', - joyride: { - title: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TITLE'), - text: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TEXT') - } - }, - { - element: '.watching-container', - position: 'right', - joyride: { - title: @translate.instant('JOYRIDE.DASHBOARD.STEP3.TITLE') - text: [ - @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT1'), - @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT2') - ] - } - }, - { - element: '.project-list .see-more-projects-btn', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.DASHBOARD.STEP4.TITLE') - text: [ - @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT1'), - @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT2') - ] - } - } - ] - - backlog: () -> - return [ - { - element: '.summary', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.BACKLOG.STEP1.TITLE') - text: [ - @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT1'), - @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT2') - ] - } - }, - { - element: '.backlog-table-empty', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.BACKLOG.STEP2.TITLE') - text: @translate.instant('JOYRIDE.BACKLOG.STEP2.TEXT') - } - }, - { - element: '.sprints', - position: 'left', - joyride: { - title: @translate.instant('JOYRIDE.BACKLOG.STEP3.TITLE') - text: @translate.instant('JOYRIDE.BACKLOG.STEP3.TEXT') - } - }, - { - element: '.new-us', - position: 'rigth', - joyride: { - title: @translate.instant('JOYRIDE.BACKLOG.STEP4.TITLE') - text: @translate.instant('JOYRIDE.BACKLOG.STEP4.TEXT') - } - } - ] - - kanban: () -> - return [ - { - element: '.kanban-table-inner', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.KANBAN.STEP1.TITLE') - text: @translate.instant('JOYRIDE.KANBAN.STEP1.TEXT') - } - }, - { - element: '.card-placeholder', - position: 'right', - joyride: { - title: @translate.instant('JOYRIDE.KANBAN.STEP2.TITLE') - text: @translate.instant('JOYRIDE.KANBAN.STEP2.TEXT') - } - }, - { - element: '.icon-plus', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') - text: [ - @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT1'), - @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT2'), - ] - } - } - ] -} - - class JoyRideService extends taiga.Service @.$inject = [ - '$translate' + '$translate', + 'tgCheckPermissionsService' ] - constructor: (@translate) -> + constructor: (@translate, @checkPermissionsService) -> + + getConfig: () -> + return { + dashboard: () => + return [ + { + element: '.project-list > section:not(.ng-hide)', + position: 'left', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TITLE'), + text: @translate.instant('JOYRIDE.DASHBOARD.STEP1.TEXT') + } + }, + { + element: '.working-on-container', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TITLE'), + text: @translate.instant('JOYRIDE.DASHBOARD.STEP2.TEXT') + } + }, + { + element: '.watching-container', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP3.TITLE') + text: [ + @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT1'), + @translate.instant('JOYRIDE.DASHBOARD.STEP3.TEXT2') + ] + } + }, + { + element: '.project-list .see-more-projects-btn', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.DASHBOARD.STEP4.TITLE') + text: [ + @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT1'), + @translate.instant('JOYRIDE.DASHBOARD.STEP4.TEXT2') + ] + } + } + ] + + backlog: () => + steps = [ + { + element: '.summary', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP1.TITLE') + text: [ + @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT1'), + @translate.instant('JOYRIDE.BACKLOG.STEP1.TEXT2') + ] + } + }, + { + element: '.backlog-table-empty', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP2.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP2.TEXT') + } + }, + { + element: '.sprints', + position: 'left', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP3.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP3.TEXT') + } + } + ] + + if @checkPermissionsService.check('add_us') + steps.push({ + element: '.new-us', + position: 'rigth', + joyride: { + title: @translate.instant('JOYRIDE.BACKLOG.STEP4.TITLE') + text: @translate.instant('JOYRIDE.BACKLOG.STEP4.TEXT') + } + }) + + return steps + + kanban: () => + return [ + { + element: '.kanban-table-inner', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP1.TITLE') + text: @translate.instant('JOYRIDE.KANBAN.STEP1.TEXT') + } + }, + { + element: '.card-placeholder', + position: 'right', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP2.TITLE') + text: @translate.instant('JOYRIDE.KANBAN.STEP2.TEXT') + } + }, + { + element: '.icon-plus', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') + text: [ + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT1'), + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT2'), + ] + } + } + ] + } get: (name) -> + joyRides = @.getConfig() joyRide = joyRides[name].call(this) return _.map joyRide, (item) -> diff --git a/app/modules/components/joy-ride/joy-ride.service.spec.coffee b/app/modules/components/joy-ride/joy-ride.service.spec.coffee index eba61f97..75bca72a 100644 --- a/app/modules/components/joy-ride/joy-ride.service.spec.coffee +++ b/app/modules/components/joy-ride/joy-ride.service.spec.coffee @@ -9,6 +9,15 @@ describe "tgJoyRideService", -> provide.value "$translate", mocks.translate + _mockCheckPermissionsService = () -> + mocks.checkPermissionsService = { + check: sinon.stub() + } + + mocks.checkPermissionsService.check.returns(true) + + provide.value "tgCheckPermissionsService", mocks.checkPermissionsService + _inject = (callback) -> inject (_tgJoyRideService_) -> joyRideService = _tgJoyRideService_ @@ -18,6 +27,7 @@ describe "tgJoyRideService", -> module ($provide) -> provide = $provide _mockTranslate() + _mockCheckPermissionsService() return null _setup = -> @@ -30,7 +40,7 @@ describe "tgJoyRideService", -> it "get joyride by category", () -> example = { - element: '.home-project-list', + element: '.project-list > section:not(.ng-hide)', position: 'left', joyride: { title: 'test', diff --git a/app/modules/services/check-permissions.service.coffee b/app/modules/services/check-permissions.service.coffee new file mode 100644 index 00000000..0b255196 --- /dev/null +++ b/app/modules/services/check-permissions.service.coffee @@ -0,0 +1,13 @@ +taiga = @.taiga + +class ChekcPermissionsService + @.$inject = [ + "tgProjectService" + ] + + constructor: (@projectService) -> + + check: (permission) -> + return @projectService.project.get('my_permissions').indexOf(permission) != -1 + +angular.module("taigaCommon").service("tgCheckPermissionsService", ChekcPermissionsService) diff --git a/app/modules/services/check-permissions.service.spec.coffee b/app/modules/services/check-permissions.service.spec.coffee new file mode 100644 index 00000000..0e862c1e --- /dev/null +++ b/app/modules/services/check-permissions.service.spec.coffee @@ -0,0 +1,44 @@ +describe "tgCheckPermissionsService", -> + checkPermissionsService = provide = null + mocks = {} + + _mockProjectService = () -> + mocks.projectService = { + project: sinon.stub() + } + + provide.value "tgProjectService", mocks.projectService + + _inject = () -> + inject (_tgCheckPermissionsService_) -> + checkPermissionsService = _tgCheckPermissionsService_ + + _mocks = () -> + module ($provide) -> + provide = $provide + _mockProjectService() + + return null + + beforeEach -> + module "taigaCommon" + _mocks() + _inject() + + it "the user has perms", () -> + mocks.projectService.project = Immutable.fromJS({ + my_permissions: ['add_us'] + }) + + perm = checkPermissionsService.check('add_us') + + expect(perm).to.be.true + + it "the user hasn't perms", () -> + mocks.projectService.project = Immutable.fromJS({ + my_permissions: [] + }) + + perm = checkPermissionsService.check('add_us') + + expect(perm).to.be.false From 12f7ef7e519e4da9a0301eb602fcaf856a49c078 Mon Sep 17 00:00:00 2001 From: Juanfran Date: Mon, 19 Oct 2015 08:23:52 +0200 Subject: [PATCH 9/9] conditional kanban joyride step if the user has creation perms --- .../joy-ride/joy-ride.service.coffee | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/app/modules/components/joy-ride/joy-ride.service.coffee b/app/modules/components/joy-ride/joy-ride.service.coffee index 39e16c70..30c69a56 100644 --- a/app/modules/components/joy-ride/joy-ride.service.coffee +++ b/app/modules/components/joy-ride/joy-ride.service.coffee @@ -80,7 +80,7 @@ class JoyRideService extends taiga.Service } } ] - + if @checkPermissionsService.check('add_us') steps.push({ element: '.new-us', @@ -94,7 +94,7 @@ class JoyRideService extends taiga.Service return steps kanban: () => - return [ + steps = [ { element: '.kanban-table-inner', position: 'bottom', @@ -110,19 +110,23 @@ class JoyRideService extends taiga.Service title: @translate.instant('JOYRIDE.KANBAN.STEP2.TITLE') text: @translate.instant('JOYRIDE.KANBAN.STEP2.TEXT') } - }, - { - element: '.icon-plus', - position: 'bottom', - joyride: { - title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') - text: [ - @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT1'), - @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT2'), - ] - } } ] + + if @checkPermissionsService.check('add_us') + steps.push({ + element: '.icon-plus', + position: 'bottom', + joyride: { + title: @translate.instant('JOYRIDE.KANBAN.STEP3.TITLE') + text: [ + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT1'), + @translate.instant('JOYRIDE.KANBAN.STEP3.TEXT2'), + ] + } + }) + + return steps } get: (name) ->