From fb4c8545dcafb1fc8ea109c63f333300859ae602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Thu, 3 Mar 2016 09:35:14 +0100 Subject: [PATCH] New creation project wizard Create project wizard layout Adding is_private attribute on create projects WIP wizard create project Wizard create WIP Wizard create public projects Add restrictions to create project Layout for common platforms remove more info button until UX issue gets resolved Add to changelog --- CHANGELOG.md | 1 + app/coffee/modules/base/navurls.coffee | 6 +- ...reate-project-restriction.directive.coffee | 8 + app/coffee/modules/projects/lightboxes.coffee | 49 +--- app/locales/taiga/locale-en.json | 18 +- app/modules/projects/projects.service.coffee | 2 +- .../projects/projects.service.spec.coffee | 2 +- .../project/wizard-create-project.jade | 169 +++++++----- app/partials/project/wizard-restrictions.jade | 9 + app/styles/modules/common/wizard.scss | 260 ++++++++---------- app/svg/sprite.svg | 6 +- 11 files changed, 255 insertions(+), 275 deletions(-) create mode 100644 app/coffee/modules/projects/create-project-restriction.directive.coffee create mode 100644 app/partials/project/wizard-restrictions.jade diff --git a/CHANGELOG.md b/CHANGELOG.md index dfe2d59b..5ba53a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Redesign 'Admin > Project > Modules' panel. - Add badge to project owners - Limit of user per project. +- Redesign of the create project wizard ### Misc - Lots of small and not so small bugfixes. diff --git a/app/coffee/modules/base/navurls.coffee b/app/coffee/modules/base/navurls.coffee index 85fee430..28fa20de 100644 --- a/app/coffee/modules/base/navurls.coffee +++ b/app/coffee/modules/base/navurls.coffee @@ -59,7 +59,7 @@ module.service("$tgNavUrls", NavigationUrlsService) ## Navigation Urls Directive ############################################################################# -NavigationUrlsDirective = ($navurls, $auth, $q, $location) -> +NavigationUrlsDirective = ($navurls, $auth, $q, $location, lightboxService) -> # Example: # link(tg-nav="project-backlog:project='sss',") @@ -157,9 +157,11 @@ NavigationUrlsDirective = ($navurls, $auth, $q, $location) -> when 2 window.open fullUrl + lightboxService.closeAll() + $scope.$on "$destroy", -> $el.off() return {link: link} -module.directive("tgNav", ["$tgNavUrls", "$tgAuth", "$q", "$tgLocation", NavigationUrlsDirective]) +module.directive("tgNav", ["$tgNavUrls", "$tgAuth", "$q", "$tgLocation", "lightboxService", NavigationUrlsDirective]) diff --git a/app/coffee/modules/projects/create-project-restriction.directive.coffee b/app/coffee/modules/projects/create-project-restriction.directive.coffee new file mode 100644 index 00000000..66564563 --- /dev/null +++ b/app/coffee/modules/projects/create-project-restriction.directive.coffee @@ -0,0 +1,8 @@ +module = angular.module("taigaProject") + +createProjectRestrictionDirective = () -> + return { + templateUrl: "project/wizard-restrictions.html" + } + +module.directive('tgCreateProjectRestriction', [createProjectRestrictionDirective]) diff --git a/app/coffee/modules/projects/lightboxes.coffee b/app/coffee/modules/projects/lightboxes.coffee index c5561ae8..774bb866 100644 --- a/app/coffee/modules/projects/lightboxes.coffee +++ b/app/coffee/modules/projects/lightboxes.coffee @@ -29,12 +29,16 @@ debounce = @.taiga.debounce module = angular.module("taigaProject") -CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, $loading, lightboxService, $cacheFactory, $translate, currentUserService) -> +CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $projectUrl, $loading, lightboxService, $cacheFactory, $translate, currentUserService, $auth) -> link = ($scope, $el, attrs) -> $scope.data = {} $scope.templates = [] currentLoading = null + $auth.refresh() + $scope.canCreatePrivateProjects = currentUserService.canCreatePrivateProjects() + $scope.canCreatePublicProjects = currentUserService.canCreatePublicProjects() + form = $el.find("form").checksley({"onlyOneErrorElement": true}) onSuccessSubmit = (response) -> @@ -58,10 +62,6 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project selectors = [] for error_field in _.keys(response) selectors.push("[name=#{error_field}]") - $el.find(".active").removeClass("active") - error_step = $el.find(selectors.join(",")).first().parents(".wizard-step") - error_step.addClass("active") - $el.find('.progress-bar').removeClass().addClass('progress-bar').addClass(error_step.data("step")) submit = (event) => event.preventDefault() @@ -77,7 +77,9 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project promise.then(onSuccessSubmit, onErrorSubmit) openLightbox = -> - $scope.data = {} + $scope.data = { + is_private: false + } if !$scope.templates.length $rs.projects.templates().then (result) => @@ -86,40 +88,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project else $scope.data.creation_template = _.head(_.filter($scope.templates, (x) -> x.slug == "scrum")).id - $el.find(".active").removeClass("active") - $el.find(".create-step1").addClass("active") - lightboxService.open($el) - timeout 600, -> - $el.find(".progress-bar").addClass('step1') - - $el.on "click", ".button-next", (event) -> - event.preventDefault() - - current = $el.find(".active") - - valid = true - for field in form.fields - if current.find("[name=#{field.element.attr('name')}]").length - valid = field.validate() != false and valid - - if not valid - return - - next = current.next() - current.toggleClass('active') - next.toggleClass('active') - step = next.data('step') - $el.find('.progress-bar').removeClass().addClass('progress-bar').addClass(step) - - $el.on "click", ".button-prev", (event) -> - event.preventDefault() - current = $el.find(".active") - prev = current.prev() - current.toggleClass('active') - prev.toggleClass('active') - step = prev.data('step') - $el.find('.progress-bar').removeClass().addClass('progress-bar').addClass(step) submitButton = $el.find(".submit-button") @@ -145,7 +114,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project module.directive("tgLbCreateProject", ["$rootScope", "$tgRepo", "$tgConfirm", "$location", "$tgNavUrls", "$tgResources", "$projectUrl", "$tgLoading", - "lightboxService", "$cacheFactory", "$translate", "tgCurrentUserService", CreateProject]) + "lightboxService", "$cacheFactory", "$translate", "tgCurrentUserService", "$tgAuth", CreateProject]) ############################################################################# diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index 368d9784..608e67fa 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -470,9 +470,9 @@ "CHANGE_LOGO": "Change logo", "ACTION_USE_DEFAULT_LOGO": "Use default image", "MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects", - "MAX_PRIVATE_PROJECTS_MEMBERS": "The project exceeds the maximun members number in private projects", + "MAX_PRIVATE_PROJECTS_MEMBERS": "The project exceeds the maximum members number in private projects", "MAX_PUBLIC_PROJECTS": "You've reached the maximum number of public projects", - "MAX_PUBLIC_PROJECTS_MEMBERS": "The project exceeds the maximun members number in public projects" + "MAX_PUBLIC_PROJECTS_MEMBERS": "The project exceeds the maximum members number in public projects" }, "REPORTS": { "TITLE": "Reports", @@ -1324,12 +1324,18 @@ } }, "WIZARD": { - "SECTION_TITLE_CHOOSE_TEMPLATE": "Choose a template", - "CHOOSE_TEMPLATE_TEXT": "Which template would fit better in your project?", "SECTION_TITLE_CREATE_PROJECT": "Create Project", "CREATE_PROJECT_TEXT": "Fresh and clean. So exciting!", - "PROGRESS_TEMPLATE_SELECTION": "Template selection", - "PROGRESS_NAME_DESCRIPTION": "Name and description" + "CHOOSE_TEMPLATE": "Which template would fit better in your project?", + "CHOOSE_TEMPLATE_TITLE": "More info about project templates", + "CHOOSE_TEMPLATE_INFO": "More info", + "PROJECT_DETAILS": "Project Details", + "PUBLIC_PROJECT": "Public Project", + "PRIVATE_PROJECT": "Private Project", + "CREATE_PROJECT": "Create project", + "MAX_PRIVATE_PROJECTS": "You've reached the maximum number of private projects", + "MAX_PUBLIC_PROJECTS": "You've reached the maximum number of public projects", + "CHANGE_PLANS": "change plans" }, "WIKI": { "PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}", diff --git a/app/modules/projects/projects.service.coffee b/app/modules/projects/projects.service.coffee index a7c2ab65..5aca481e 100644 --- a/app/modules/projects/projects.service.coffee +++ b/app/modules/projects/projects.service.coffee @@ -57,7 +57,7 @@ class ProjectsService extends taiga.Service newProject: -> @lightboxFactory.create("tg-lb-create-project", { - "class": "wizard-create-project" + "class": "wizard-create-project lightbox" }) bulkUpdateProjectsOrder: (sortData) -> diff --git a/app/modules/projects/projects.service.spec.coffee b/app/modules/projects/projects.service.spec.coffee index 046d1d23..6b49303b 100644 --- a/app/modules/projects/projects.service.spec.coffee +++ b/app/modules/projects/projects.service.spec.coffee @@ -79,7 +79,7 @@ describe "tgProjectsService", -> projectsService.newProject() expect(mocks.lightboxFactory.create).to.have.been.calledWith("tg-lb-create-project", { - "class": "wizard-create-project" + "class": "wizard-create-project lightbox" }) it "bulkUpdateProjectsOrder and then fetch projects again", () -> diff --git a/app/partials/project/wizard-create-project.jade b/app/partials/project/wizard-create-project.jade index f32cc8c0..ca9808be 100644 --- a/app/partials/project/wizard-create-project.jade +++ b/app/partials/project/wizard-create-project.jade @@ -1,77 +1,94 @@ +svg.close.icon.icon-close(title="{{'COMMON.CLOSE' | translate}}") + use(xlink:href="#icon-close") form - section.wizard-step.create-step1(data-step="step1") - div.title - h1(translate="WIZARD.SECTION_TITLE_CHOOSE_TEMPLATE") - p(translate="WIZARD.CHOOSE_TEMPLATE_TEXT") - div.template-wrapper - div.template-inner - fieldset(ng-repeat="template in templates") - input( - type="radio" - name="template" - id="template-{{ template.id }}" - ng-value='template.id' - ng-model="data.creation_template" - data-required="true" - ) - label.backlog(for="template-{{ template.id }}") - svg.icon(ng-class="'icon-'+template.slug") - use(xlink:href="{{'#icon-' + template.slug }}") - h2 {{ template.name }} - p {{ template.description }} - - fieldset - a.button-next.button-green( - href="#" - title="{{'PAGINATION.NEXT' | translate}}" - translate="PAGINATION.NEXT" - ) - - section.wizard-step.create-step2.active(data-step="step2") - div.title - h1(translate="WIZARD.SECTION_TITLE_CREATE_PROJECT") - p(translate="WIZARD.CREATE_PROJECT_TEXT") - div.template-wrapper - div.template-inner - fieldset - input( - type="text" - name="name" - ng-model="data.name" - data-required="true" - placeholder="{{'COMMON.FIELDS.NAME' | translate}}" - maxlength="45" - ) - fieldset - textarea( - name="description" - ng-model="data.description" - data-required="true" - ng-attr-placeholder="{{'COMMON.FIELDS.DESCRIPTION' | translate}}" - ) - fieldset.wizard-action - div - a.button-prev.button.button-gray( - href="#" - title="{{'PAGINATION.PREVIOUS' | translate}}" - translate="PAGINATION.PREVIOUS" - ) - button.button-green.submit-button( - type="submit" - title="{{'COMMON.CREATE' | translate}}" - translate="COMMON.CREATE" - ) - - button.hidden(type="submit") - -div.progress-bar - div.progress-state - span(translate="WIZARD.PROGRESS_TEMPLATE_SELECTION") - span(translate="WIZARD.PROGRESS_NAME_DESCRIPTION") - // span Final touches - div.progress-bar-wrapper - div.bar - -a.close(href="#" title="{{'COMMON.CLOSE' | translate}}") - svg.icon.icon-delete - use(xlink:href="#icon-delete") + header + h1.title(translate="WIZARD.SECTION_TITLE_CREATE_PROJECT") + .subtitle( + translate="WIZARD.CREATE_PROJECT_TEXT" + role="presentation" + ) + section.template-option + .template-selector-title + legend(translate="WIZARD.CHOOSE_TEMPLATE") + // UX issue + //- a.more-info( + //- href="" + //- title="{{ 'WIZARD.CHOOSE_TEMPLATE_TITLE' | translate }}" + //- translate="WIZARD.CHOOSE_TEMPLATE_INFO" + //- ) + .template-selector + fieldset(ng-repeat="template in templates") + input( + type="radio" + name="template" + id="template-{{ template.id }}" + ng-value='template.id' + ng-model="data.creation_template" + data-required="true" + ) + label.template-label(for="template-{{ template.id }}") + svg.icon(ng-class="'icon-'+template.slug") + use(xlink:href="{{'#icon-' + template.slug }}") + span.template-name {{ template.name }} + .template-data + legend(translate="WIZARD.PROJECT_DETAILS") + fieldset + input( + type="text" + name="name" + ng-model="data.name" + data-required="true" + placeholder="{{'COMMON.FIELDS.NAME' | translate}}" + maxlength="45" + aria-hidden="true" + ) + fieldset + textarea( + name="description" + ng-model="data.description" + data-required="true" + ng-attr-placeholder="{{'COMMON.FIELDS.DESCRIPTION' | translate}}" + ) + .template-privacity + fieldset + input( + type="radio" + name="is_private" + id="template-public" + data-required="true" + aria-hidden="true" + ng-value="false" + ng-model="data.is_private" + required + ng-disabled="!canCreatePublicProjects.valid" + ng-checked="canCreatePublicProjects.valid" + ) + label.template-privacity(for="template-public") + svg.icon.icon-discover + use(xlink:href="#icon-discover") + span(translate="WIZARD.PUBLIC_PROJECT") + fieldset + input( + type="radio" + name="is_private" + id="template-private" + data-required="true" + ng-value="true" + ng-model="data.is_private" + aria-hidden="true" + required + ng-disabled="!canCreatePrivateProjects.valid" + ng-checked="!canCreatePublicProjects.valid" + ) + label.template-privacity(for="template-private") + svg.icon.icon-lock + use(xlink:href="#icon-lock") + span(translate="WIZARD.PRIVATE_PROJECT") + + tg-create-project-restriction + + button.button-green.submit-button( + translate="WIZARD.CREATE_PROJECT" + title="{{'WIZARD.CREATE_PROJECT' | translate}}" + ng-click="" + ) diff --git a/app/partials/project/wizard-restrictions.jade b/app/partials/project/wizard-restrictions.jade new file mode 100644 index 00000000..798bf1fd --- /dev/null +++ b/app/partials/project/wizard-restrictions.jade @@ -0,0 +1,9 @@ +div.create-warning(ng-if="!canCreatePrivateProjects.valid && canCreatePrivateProjects.reason == 'max_private_projects'") + svg.icon.icon-exclamation + use(xlink:href="#icon-exclamation") + span {{ 'WIZARD.MAX_PRIVATE_PROJECTS' | translate }} + +div.create-warning(ng-if="!canCreatePublicProjects.valid && canCreatePublicProjects.reason == 'max_public_projects'") + svg.icon.icon-exclamation + use(xlink:href="#icon-exclamation") + span {{ 'WIZARD.MAX_PUBLIC_PROJECTS' | translate }} diff --git a/app/styles/modules/common/wizard.scss b/app/styles/modules/common/wizard.scss index 494c9048..81a1b6c0 100644 --- a/app/styles/modules/common/wizard.scss +++ b/app/styles/modules/common/wizard.scss @@ -1,73 +1,109 @@ .wizard-create-project { @extend %lightbox; - @extend %background-taiga; - background-size: cover; - color: $white; - text-align: center; - form { - width: 500px; - } - .title { - width: 100%; - } - h1, - p { - color: $white; - } - h1 { - line-height: 1.5rem; - } - p { - @extend %small; - opacity: .8; - } - input, - textarea, - select { - background: rgba($white, .7); - @include placeholder { - color: $grayer; - } - } .close { - color: $white; - &:hover { - color: $red-light; + @include svg-size(2rem); + } + form { + width: 700px; + } + header { + margin-bottom: 3rem; + .title { + margin-bottom: 0; + } + .subtitle { + @extend %small; + color: $gray-light; + text-align: center; } } - .wizard-step { - animation: formSlide .4s ease-in-out; - animation-direction: alternate-reverse; - display: none; - &.active { - animation: formSlide .4s ease-in-out; - &.create-step2, - &.create-step3, - &.create-step1 { - display: block; - } - } + .more-info { + @extend %small; + color: $primary; } - .wizard-action { - div { - display: flex; + .template-selector-title { + display: flex; + justify-content: space-between; + margin-bottom: 1rem; + } + .template-selector { + display: flex; + margin-bottom: 1rem; + input { + display: none; } - a { - color: $white; - display: inline-block; - flex-basis: 40%; - flex-grow: 1; + fieldset { &:first-child { margin-right: .5rem; } } } - .create-step1 { - .template-inner { - display: flex; + input { + &:checked+label { + background: $primary-light; + color: $white; + transition: background .2s ease-in; + &:hover { + background: $primary-light; + } } + +label { + background: rgba($whitish, .7); + cursor: pointer; + display: block; + padding: 2rem 1rem; + text-align: center; + transition: background .2s ease-in; + &:hover { + background: rgba($primary-light, .3); + transition: background .2s ease-in; + } + .icon { + @include svg-size(1.4rem); + fill: currentColor; + margin-right: 1rem; + vertical-align: text-top; + } + .template-name { + @extend %large; + text-transform: uppercase; + } + } + } + input[disabled]+label { + background: lighten($whitish, 5%); + box-shadow: none; + color: lighten($gray-light, 20%); + cursor: not-allowed; + opacity: .65; + &:hover { + background: lighten($whitish, 5%); + color: lighten($gray-light, 20%); + } + } + .template-data { + legend { + display: block; + margin-bottom: .5rem; + } + input, + textarea { + background: none; + border: 1px solid $whitish; + color: $gray-light; + @include placeholder { + color: darken($whitish, 20%); + } + } + textarea { + height: 7rem; + min-height: 7rem; + } + } + .template-privacity { + display: flex; fieldset { - flex-grow: 1; + margin-bottom: 0; &:first-child { margin-right: .5rem; } @@ -75,105 +111,37 @@ input { display: none; } - input:checked { - +label { - background: rgba($primary-light, .7); - transition: background .3s ease-in; - } + label { + display: block; + text-align: center; + text-transform: uppercase; + } input+label { - background: rgba($whitish, .7); - cursor: pointer; - display: block; - margin-bottom: 1rem; padding: 1rem; - text-align: center; - transition: background .3s ease-in; - &:hover { - background: rgba($primary, .7); - transition: background .3s ease-in; - } - .icon { - @include svg-size(3rem); - fill: currentColor; - } } - h2 { - color: $white; - margin: 0; - margin-top: .5rem; - text-transform: uppercase; - } - p { - text-align: center; + svg { + @include svg-size(.7rem); } } - .progress-bar { - bottom: 0; - height: .5rem; - left: 0; - position: absolute; - width: 100%; - } - .step1 { - .bar { - transition: width .6s ease-in-out; - width: 25%; + .create-warning { + @extend %small; + padding: 1rem; + text-align: center; + .icon-exclamation { + fill: $red-light; + margin-right: .5rem; + vertical-align: middle; } - .progress-state { - span:nth-child(1) { - color: rgba($white, 1); - transition: color .3s ease-in-out; - transition-delay: .6s; - } - } - } - .step2 { - .bar { - transition: width .6s ease-in-out; - // width: 50%; - width: 75%; - } - .progress-state { - span:nth-child(1), - span:nth-child(2) { - color: rgba($white, 1); - transition: color .3s ease-in-out; - transition-delay: .6s; - } - } - } - - .progress-state { - position: absolute; - width: 100%; - span { - color: rgba($white, .5); + a { + color: $primary; display: inline-block; - margin-left: -100px; - position: absolute; - text-align: center; - top: -2rem; - transition: all 1s ease-in; - width: 200px; - &:nth-child(1) { - left: 25%; - } - &:nth-child(2) { - left: 75%; - } + margin-left: .25rem; } } - .progress-bar-wrapper { - background: rgba($white, .3); - height: .5rem; - } - .bar { - background: rgba($primary-light, .9); - height: .5rem; - left: 0; - position: absolute; - top: 0; - width: 0; + .button-green { + display: block; + margin: 1rem 5rem; + width: calc(100% - 10rem); } } diff --git a/app/svg/sprite.svg b/app/svg/sprite.svg index 2a25e0c5..79330287 100644 --- a/app/svg/sprite.svg +++ b/app/svg/sprite.svg @@ -413,10 +413,10 @@ fill="#9dce0a" d="M512.053 24.761l-130.433 173.609-213.936-30.392 30.427 214.125-173.349 130.201 173.349 130.2-30.427 214.123 214.747-30.506 130.064 173.118 129.997-173.027 214.355 30.451-30.588-215.258 172.981-129.922-172.497-129.561 30.458-214.345-214.912 30.53-130.235-173.346z"> - - team-question + + Exclamation + d="M512 0c-282.394 0-512 229.606-512 512s229.606 512 512 512 512-229.606 512-512-229.606-512-512-512zM512 64c247.803 0 448 200.197 448 448s-200.197 448-448 448-448-200.197-448-448 200.197-448 448-448zM275.256 230.001l-45.256 45.251 236.744 236.749-236.744 236.744 45.256 45.256 236.744-236.744 236.744 236.744 45.256-45.256-236.744-236.744 236.744-236.749-45.253-45.251-236.749 236.744z"> Blocked Project