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
stable
Xavier Julián 2016-03-03 09:35:14 +01:00 committed by Juanfran
parent 090a591074
commit fb4c8545dc
11 changed files with 255 additions and 275 deletions

View File

@ -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.

View File

@ -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])

View File

@ -0,0 +1,8 @@
module = angular.module("taigaProject")
createProjectRestrictionDirective = () ->
return {
templateUrl: "project/wizard-restrictions.html"
}
module.directive('tgCreateProjectRestriction', [createProjectRestrictionDirective])

View File

@ -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])
#############################################################################

View File

@ -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}}",

View File

@ -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) ->

View File

@ -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", () ->

View File

@ -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=""
)

View File

@ -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 }}

View File

@ -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);
}
}

View File

@ -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"></path>
</symbol>
<symbol id="icon-team-question" viewBox="0 0 1024 1024">
<title>team-question</title>
<symbol id="icon-exclamation" viewBox="0 0 1024 1024">
<title>Exclamation</title>
<path
d="M165 91.5c-33 0-60 28.7-60 64 0 35.2 27 64 60 64s60-28.8 60-64c0-35.3-27-64-60-64zm0 16.2c24.8 0 44.8 21.3 44.8 47.8 0 26.5-20 47.8-44.8 47.8-25 0-45-21.3-45-47.8 0-26.5 20-47.8 45-47.8zm-77.8 13.5c-29.2 0-53 25.5-53 56.7 0 31 23.8 56.5 53 56.5 16 0 27.5-4.5 37.3-16.6-21.8-9.7-34-36-34-62.5 0-12 3-23.3 8.5-33-3.8-.8-7.7-1.3-11.8-1.3zM165 229.7c-53.3 0-95.8 50.8-96 112.5v8h192v-8c-.2-61.7-42.8-112.5-96-112.5zm-78.7 13.6c-3.4 0-5.8.3-5.8.3-47.2 0-78 45-78.3 99.5v7.3h52.2v-8c0-41.3 15.2-78.6 43.7-98.2-3.8-.6-8.2-.8-11.7-.7zM165 246c42 0 76.8 38.2 80.5 88h-161c3.6-49.8 38.6-88 80.5-88zM339.3 49.7C307 49.7 281 76 281 108.2c0 32.2 26 58.4 58.3 58.4s58.5-26.2 58.5-58.4c0-32.3-26.2-58.5-58.5-58.5zm0 7.3c28.3 0 51.2 23 51.2 51.2 0 28.3-23 51-51.2 51-28.3 0-51-22.7-51-51S311 57 339.2 57zm-.2 11.8c-7 0-14 1.7-20.5 5l3 7c3.5-1.6 6.5-2.7 9-3.3 2.7-.7 5.4-1 8-1 4.4 0 7.6 1 10 3 2.2 1.8 3.3 4.5 3.3 8 0 3-.6 5.4-1.8 7.4-1.2 2-4.2 5-9 9-3.3 2.8-5.7 5.6-7 8.2-1.2 2.6-1.8 6-1.8 10v2.8h6.7v-1.7c0-3.3.5-6 1.6-8 1-2 3.5-4.4 7-7.3 4.4-4 7.2-6.6 8.5-8.3 1.4-1.8 2.4-3.6 3-5.5.8-2 1-4.2 1-6.7 0-6-1.7-10.6-5.4-13.8-3.7-3.3-8.8-5-15.5-5zm-6.8 62.5V148h7.3v-16.7h-7.3z"></path>
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"></path>
</symbol>
<symbol id="icon-blocked-project" viewBox="0 0 1024 1024">
<title>Blocked Project</title>

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB