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 += "
#{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
' + } + + 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",