Merge pull request #643 from taigaio/improving-placeholders

Improving placeholders
stable
Alejandro 2015-10-15 14:59:09 +02:00
commit daec7312c0
47 changed files with 539 additions and 226 deletions

View File

@ -61,6 +61,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
@scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME")
@showTags = false
@activeFilters = false
@scope.showGraphPlaceholder = null
@.initializeEventHandlers()
@ -146,12 +147,14 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
loadProjectStats: ->
return @rs.projects.stats(@scope.projectId).then (stats) =>
@scope.stats = stats
totalPoints = if stats.total_points then stats.total_points else stats.defined_points
if stats.total_points
@scope.stats.completedPercentage = Math.round(100 * stats.closed_points / stats.total_points)
if totalPoints
@scope.stats.completedPercentage = Math.round(100 * stats.closed_points / totalPoints)
else
@scope.stats.completedPercentage = 0
@scope.showGraphPlaceholder = !(stats.total_points? && stats.total_milestones?)
return stats
unloadClosedSprints: ->
@ -179,7 +182,10 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
return @rs.sprints.list(@scope.projectId, params).then (result) =>
sprints = result.milestones
@scope.totalMilestones = sprints
@scope.totalClosedMilestones = result.closed
@scope.totalOpenMilestones = result.open
@scope.totalMilestones = @scope.totalOpenMilestones + @scope.totalClosedMilestones
# NOTE: Fix order of USs because the filter orderBy does not work propertly in partials files
for sprint in sprints
@ -293,7 +299,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
newSprint = @scope.sprintsById[newSprintId]
# Move to closed sprint
if !newSprint
if !newSprint && newSprintId
newSprint = @scope.closedSprintsById[newSprintId]
movedToClosedSprint = true if newSprint
@ -575,7 +581,7 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
linkDoomLine = ($scope, $el, $attrs, $ctrl) ->
reloadDoomLine = ->
if $scope.stats?
if $scope.stats? and $scope.stats.total_points? and $scope.stats.total_points != 0
removeDoomlineDom()
stats = $scope.stats
@ -699,12 +705,12 @@ BacklogDirective = ($repo, $rootscope, $translate) ->
elm.addClass("active")
text = $translate.instant("BACKLOG.TAGS.HIDE")
elm.find(".text").text(text)
elm.text(text)
else
elm.removeClass("active")
text = $translate.instant("BACKLOG.TAGS.SHOW")
elm.find(".text").text(text)
elm.text(text)
showHideFilter = ($scope, $el, $ctrl) ->
sidebar = $el.find("sidebar.filters-bar")
@ -929,18 +935,36 @@ module.directive("tgBacklogUsPoints", ["$tgEstimationsService", "$tgRepo", "$tgT
## Burndown graph directive
#############################################################################
ToggleBurndownVisibility = ($storage) ->
hide = () ->
$(".js-burndown-graph").removeClass("shown")
$(".js-toggle-burndown-visibility-button").removeClass("active")
$(".js-burndown-graph").removeClass("open")
show = (firstLoad) ->
$(".js-toggle-burndown-visibility-button").addClass("active")
if firstLoad
$(".js-burndown-graph").addClass("shown")
else
$(".js-burndown-graph").addClass("open")
link = ($scope, $el, $attrs) ->
firstLoad = true
hash = generateHash(["is-burndown-grpahs-collapsed"])
$scope.isBurndownGraphCollapsed = $storage.get(hash) or false
toggleGraph = ->
if $scope.isBurndownGraphCollapsed
$(".js-toggle-burndown-visibility-button").removeClass("active")
$(".js-burndown-graph").removeClass("open")
hide(firstLoad)
else
$(".js-toggle-burndown-visibility-button").addClass("active")
$(".js-burndown-graph").addClass("open")
show(firstLoad)
$scope.isBurndownGraphCollapsed = $storage.get(hash) or false
toggleGraph()
firstLoad = false
$scope.$watch "showGraphPlaceholder", () ->
if $scope.showGraphPlaceholder?
$scope.isBurndownGraphCollapsed = $scope.isBurndownGraphCollapsed || $scope.showGraphPlaceholder
toggleGraph()
$el.on "click", ".js-toggle-burndown-visibility-button", ->
$scope.isBurndownGraphCollapsed = !$scope.isBurndownGraphCollapsed
@ -951,7 +975,6 @@ ToggleBurndownVisibility = ($storage) ->
$el.off()
return {
scope: {}
link: link
}
@ -1110,7 +1133,7 @@ TgBacklogProgressBarDirective = ($template, $compile) ->
$scope.$watch $attrs.tgBacklogProgressBar, (stats) ->
if stats?
totalPoints = stats.total_points
totalPoints = if stats.total_points then stats.total_points else stats.defined_points
definedPoints = stats.defined_points
closedPoints = stats.closed_points
if definedPoints > totalPoints

View File

@ -144,6 +144,7 @@ BacklogEmptySortableDirective = ($repo, $rs, $rootscope) ->
# If the user has not enough permissions we don't enable the sortable
if project.my_permissions.indexOf("modify_us") > -1
$el.sortable({
items: ".us-item-row",
dropOnEmpty: true
})

View File

@ -34,14 +34,10 @@ module = angular.module("taigaKanban")
# Vars
defaultViewMode = "maximized"
defaultViewModes = {
maximized: {
cardClass: "kanban-task-maximized"
}
minimized: {
cardClass: "kanban-task-minimized"
}
}
viewModes = [
"maximized",
"minimized"
]
#############################################################################
@ -157,6 +153,10 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
usByStatus[status.id] = _.sortBy(usByStatus[status.id], "kanban_order")
if userstories.length == 0
status = @scope.usStatusList[0]
usByStatus[status.id].push({isPlaceholder: true})
@scope.usByStatus = usByStatus
# The broadcast must be executed when the DOM has been fully reloaded.
@ -221,8 +221,9 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.statusViewModes = {}
for status in @scope.usStatusList
mode = storedStatusViewModes[status.id]
@scope.statusViewModes[status.id] = if _.has(defaultViewModes, mode) then mode else defaultViewMode
mode = storedStatusViewModes[status.id] || defaultViewMode
@scope.statusViewModes[status.id] = mode
@.storeStatusViewModes()
@ -233,9 +234,13 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
@scope.statusViewModes[statusId] = newViewMode
@.storeStatusViewModes()
getCardClass: (statusId)->
isMaximized: (statusId) ->
mode = @scope.statusViewModes[statusId] or defaultViewMode
return defaultViewModes[mode].cardClass or defaultViewModes[defaultViewMode].cardClass
return mode == 'maximized'
isMinimized: (statusId) ->
mode = @scope.statusViewModes[statusId] or defaultViewMode
return mode == 'minimized'
# Utils methods
@ -414,7 +419,7 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) ->
else if not us.is_blocked and $el.hasClass("blocked")
$el.removeClass("blocked")
$el.find(".icon-edit").on "click", (event) ->
$el.on 'click', '.icon-edit', (event) ->
if $el.find(".icon-edit").hasClass("noclick")
return
@ -431,11 +436,17 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) ->
$rootscope.$broadcast("usform:edit", editingUserStory)
currentLoading.finish()
$scope.getTemplateUrl = () ->
if $scope.us.isPlaceholder
return "common/components/kanban-placeholder.html"
else
return "kanban/kanban-task.html"
$scope.$on "$destroy", ->
$el.off()
return {
templateUrl: "kanban/kanban-task.html"
template: '<ng-include src="getTemplateUrl()"/>',
link: link
require: "ngModel"
}

View File

@ -74,10 +74,7 @@ CreateProject = ($rootscope, $repo, $confirm, $location, $navurls, $rs, $project
promise.then(onSuccessSubmit, onErrorSubmit)
openLightbox = ->
$scope.data = {
total_story_points: 100
total_milestones: 5
}
$scope.data = {}
if !$scope.templates.length
$rs.projects.templates().then (result) =>

View File

@ -184,6 +184,15 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin)
if @scope.usTasks[task.user_story]? and @scope.usTasks[task.user_story][task.status]?
@scope.usTasks[task.user_story][task.status].push(task)
if tasks.length == 0
if @scope.userstories.length > 0
usId = @scope.userstories[0].id
else
usId = null
@scope.usTasks[usId][@scope.taskStatusList[0].id].push({isPlaceholder: true})
return tasks
loadTaskboard: ->

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
app/images/issues-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
app/images/search-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
app/images/sprint-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -328,6 +328,7 @@
"HOME": {
"PAGE_TITLE": "Home - Taiga",
"PAGE_DESCRIPTION": "The Taiga home page with your main projects and all your assigned and watched user stories, tasks and issues",
"EMPTY_WORKING_ON": "<strong>It feels empty, doesn't it?</strong> Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.",
"EMPTY_WATCHING": "<strong>Follow</strong> the projects, User Stories, Tasks, Issues... that you want to know about :)",
"EMPTY_PROJECT_LIST": "You don't have any projects yet",
"WORKING_ON_SECTION": "Working on",
@ -420,8 +421,8 @@
"PROJECT_DETAILS": "Project details",
"PROJECT_NAME": "Project name",
"PROJECT_SLUG": "Project slug",
"NUMBER_SPRINTS": "Number of sprints",
"NUMBER_US_POINTS": "Number of US points",
"NUMBER_SPRINTS": "Number of sprints (0 for an undetermined quantity)",
"NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)",
"TAGS": "Tags",
"DESCRIPTION": "Description",
"PUBLIC_PROJECT": "Public project",
@ -881,6 +882,10 @@
"PAGE_TITLE": "Backlog - {{projectName}}",
"PAGE_DESCRIPTION": "The backlog panel, with user stories and sprints of the project {{projectName}}: {{projectDescription}}",
"SECTION_NAME": "Backlog",
"CUSTOMIZE_GRAPH": "Customize your backlog graph",
"CUSTOMIZE_GRAPH_TEXT": "To have a nice graph that helps you follow the evolution of the project you have to set up the points and sprints through the",
"CUSTOMIZE_GRAPH_ADMIN": "Admin",
"CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin",
"MOVE_US_TO_CURRENT_SPRINT": "Move to Current Sprint",
"SHOW_FILTERS": "Show filters",
"SHOW_TAGS": "Show tags",
@ -945,8 +950,9 @@
"LINK_TASKBOARD": "Sprint Taskboard",
"TITLE_LINK_TASKBOARD": "Go to Taskboard of \"{{name}}\"",
"NUMBER_SPRINTS": "<br/>sprints",
"TITLE_ACTION_NEW_SPRINT": "+ New sprint",
"ACTION_NEW_SPRINT": "+ New sprint",
"EMPTY": "YOU HAVE NO SPRINTS",
"TITLE_ACTION_NEW_SPRINT": "Add new sprint",
"TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project",
"ACTION_SHOW_CLOSED_SPRINTS": "Show closed sprints",
"ACTION_HIDE_CLOSED_SPRINTS": "Hide closed sprints"
}
@ -967,6 +973,8 @@
"TITLE_ACTION_ADD_BULK": "Add some new Tasks in bulk",
"TITLE_ACTION_ASSIGN": "Assign task",
"TITLE_ACTION_EDIT": "Edit task",
"PLACEHOLDER_CARD_TITLE": "This could be a task",
"PLACEHOLDER_CARD_TEXT": "Split Stories into tasks to track them separately",
"TABLE": {
"COLUMN": "User story",
"TITLE_ACTION_FOLD": "Fold column",
@ -1093,8 +1101,7 @@
"TITLE_ACTION_ASSIGNED_TO": "Assigned to",
"EMPTY": {
"TITLE": "There are no issues to report :-)",
"SUBTITLE": "Did you find an issue?",
"ACTION_CREATE_ISSUE": "Create a new Issue"
"SUBTITLE": "Did you find an issue?"
}
}
},
@ -1116,7 +1123,9 @@
"ACTION_HIDE_ARCHIVED": "Hide archived",
"HIDDEN_USER_STORIES": "The user stories in this status are hidden by default",
"ARCHIVED": "You have archived",
"UNDO_ARCHIVED": "Drag & drop again to undo"
"UNDO_ARCHIVED": "Drag & drop again to undo",
"PLACEHOLDER_CARD_TITLE": "These are your User Stories",
"PLACEHOLDER_CARD_TEXT": "Stories might also have subtasks to separate requirements"
},
"SEARCH": {
"PAGE_TITLE": "Search - {{projectName}}",

View File

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

View File

@ -58,18 +58,3 @@
margin: 2rem 30%;
}
}
.watching-empty {
padding: 5vh;
text-align: center;
svg {
margin: 2rem auto;
max-width: 160px;
text-align: center;
path {
fill: $whitish;
}
}
p {
@extend %small;
}
}

View File

@ -1,20 +1,23 @@
ul.home-project-list(ng-show="vm.projects.size")
li.home-project-list-single(tg-bind-scope, tg-repeat="project in vm.projects")
a(href="#", tg-nav="project:project=project.get('slug')")
h2.home-project-list-single-title
span.project-name(title="{{ ::project.get('name') }}") {{::project.get('name')}}
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
include ../../../svg/lock.svg
p {{ ::project.get('description') | limitTo:150 }}
span(ng-if="::project.get('description').size > 150") ...
section.home-project-list(ng-show="vm.projects.size")
ul
li.home-project-list-single(tg-bind-scope, tg-repeat="project in vm.projects")
a(href="#", tg-nav="project:project=project.get('slug')")
h2.home-project-list-single-title
span.project-name(title="{{ ::project.get('name') }}") {{::project.get('name')}}
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
include ../../../svg/lock.svg
p {{ ::project.get('description') | limitTo:150 }}
span(ng-if="::project.get('description').size > 150") ...
a.see-more-projects-btn.button-gray(href="#",
ng-show="vm.projects.size",
tg-nav="projects",
title="{{'PROJECT.NAVIGATION.SEE_MORE_PROJECTS' | translate}}",
translate="PROJECT.NAVIGATION.SEE_MORE_PROJECTS")
a.see-more-projects-btn.button-gray(
href="#",
ng-show="vm.projects.size",
tg-nav="projects",
title="{{'PROJECT.NAVIGATION.SEE_MORE_PROJECTS' | translate}}",
translate="PROJECT.NAVIGATION.SEE_MORE_PROJECTS"
)
section.projects-empty(ng-hide="vm.projects.size")
section.projects-empty(ng-show="!vm.projects.size")
include ../../../svg/empty-project.svg
p(translate="HOME.EMPTY_PROJECT_LIST")
a.create-project-button.button-green(href="#", ng-click="vm.newProject()",

View File

@ -18,11 +18,11 @@
transition: fill .3s linear;
}
}
}
a {
display: flex;
flex-direction: column;
min-height: 5rem;
a {
display: flex;
flex-direction: column;
min-height: 5rem;
}
}
h2 {
@extend %text;
@ -63,6 +63,7 @@
}
p {
@extend %small;
@extend %light;
}
.create-project-button {
display: block;

View File

@ -0,0 +1,6 @@
- for (var x = 0; x < 2; x++)
.empty-ticket
.avatar
.data
.line
.line

View File

@ -0,0 +1,43 @@
.working-on-empty,
.watching-empty {
margin-bottom: 4rem;
p {
@extend %light;
margin: 2rem 9rem 1rem;
text-align: center;
}
}
.empty-ticket {
display: flex;
&:not(:last-child) {
border-bottom: 1px solid $whitish;
padding: 1rem 0;
}
&:last-child {
padding: 1rem 0 0;
}
.avatar {
background: darken($whitish, 5%);
flex-basis: 48px;
height: 48px;
margin-right: 1rem;
width: 48px;
}
.data {
display: flex;
flex-direction: column;
}
.line {
background: $whitish;
height: 1rem;
margin-bottom: 1rem;
width: 40vw;
&:last-child {
margin: 0;
width: 20vw;
}
}
}

View File

@ -1,12 +1,17 @@
div.title-bar.working-on-title(ng-show="vm.assignedTo.size", translate="HOME.WORKING_ON_SECTION")
div.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}")
section.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-empty(ng-show="!vm.watching.size")
include ../../../svg/hide.svg
p(translate="HOME.EMPTY_WATCHING")
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}")
div.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

View File

@ -29,7 +29,7 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl"
label(for="total-story-points", translate="ADMIN.PROJECT_PROFILE.NUMBER_US_POINTS")
input(type="number", name="total_story_points", min="0", placeholder="{{'ADMIN.PROJECT_PROFILE.NUMBER_US_POINTS' | translate}}",
id="total-story-points", ng-model="project.total_story_points",
data-type="digits", data-required="true")
data-type="digits")
fieldset
label(for="tags", translate="ADMIN.PROJECT_PROFILE.TAGS")

View File

@ -11,39 +11,56 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
div.backlog-summary(tg-toggle-burndown-visibility)
include ../includes/components/summary
div.graphics-container.burndown-container.js-burndown-graph
div.empty-burndown(ng-if="showGraphPlaceholder")
div.graph-icon
include ../../svg/graph.svg
div.empty-text
p.title(translate="BACKLOG.CUSTOMIZE_GRAPH")
p {{'BACKLOG.CUSTOMIZE_GRAPH_TEXT' | translate}} #[a(href="", tg-nav="project-admin-project-profile-details:project=project.slug", title="{{'BACKLOG.CUSTOMIZE_GRAPH_TITLE' | translate}}") {{'BACKLOG.CUSTOMIZE_GRAPH_ADMIN' | translate}}]
div.graphics-container.js-burndown-graph
div.burndown(tg-burndown-backlog-graph)
include ../includes/modules/burndown
div.backlog-menu
div.backlog-table-options
a.trans-button.move-to-current-sprint(href="",
title="{{'BACKLOG.MOVE_US_TO_CURRENT_SPRINT' | translate}}",
id="move-to-current-sprint")
a.trans-button.move-to-current-sprint(
href=""
title="{{'BACKLOG.MOVE_US_TO_CURRENT_SPRINT' | translate}}"
id="move-to-current-sprint"
)
span.icon.icon-move
span.text(translate="BACKLOG.MOVE_US_TO_CURRENT_SPRINT")
a.trans-button(href="",
title="{{'BACKLOG.FILTERS.TOGGLE' | translate}}",
id="show-filters-button")
span.icon.icon-filter
span.text(translate="BACKLOG.FILTERS.SHOW")
a.trans-button(href="",
title="{{'BACKLOG.TAGS.TOGGLE' | translate}}",
id="show-tags")
span.icon.icon-tag
span.text(translate="BACKLOG.TAGS.SHOW")
a.trans-button(
ng-if="userstories.length"
href=""
title="{{'BACKLOG.FILTERS.TOGGLE' | translate}}"
id="show-filters-button"
translate="BACKLOG.FILTERS.SHOW"
)
a.trans-button(
ng-if="userstories.length"
href=""
title="{{'BACKLOG.TAGS.TOGGLE' | translate}}"
id="show-tags"
translate="BACKLOG.TAGS.SHOW"
)
include ../includes/components/addnewus
section.backlog-table(ng-class="{'hidden': !userstories.length}")
include ../includes/modules/backlog-table
div.empty.empty-backlog(ng-class="{'hidden': userstories.length}", tg-backlog-empty-sortable)
span.icon.icon-backlog
span.title(translate="BACKLOG.EMPTY")
a(href="", title="{{'BACKLOG.CREATE_NEW_US' | translate}}",
ng-click="ctrl.addNewUs('standard')",
tg-check-permission="add_us",
translate="BACKLOG.CREATE_NEW_US_EMPTY_HELP")
div.empty-backlog(ng-class="{'hidden': userstories.length}", tg-backlog-empty-sortable)
img(
src="/images/backlog-empty.png"
alt="{{'BACKLOG.EMPTY' | translate}}"
)
p.title(translate="BACKLOG.EMPTY")
a(href="", title="{{'BACKLOG.CREATE_NEW_US' | translate}}"
ng-click="ctrl.addNewUs('standard')"
tg-check-permission="add_us"
translate="BACKLOG.CREATE_NEW_US_EMPTY_HELP"
)
sidebar.menu-secondary.sidebar
include ../includes/modules/sprints

View File

@ -0,0 +1,7 @@
.placeholder-avatar
.image
.text
.line
.line
p.title {{'KANBAN.PLACEHOLDER_CARD_TITLE' | translate}}
p {{'KANBAN.PLACEHOLDER_CARD_TEXT' | translate}}

View File

@ -0,0 +1,7 @@
.placeholder-avatar
.image
.text
.line
.line
p.title {{'TASKBOARD.PLACEHOLDER_CARD_TITLE' | translate}}
p {{'TASKBOARD.PLACEHOLDER_CARD_TEXT' | translate}}

View File

@ -0,0 +1,6 @@
img(
src="../../images/search-empty.png"
alt="{{ 'SEARCH.EMPTY_TITLE' | translate }}"
)
p.title {{ 'SEARCH.EMPTY_TITLE' | translate }}
p {{ 'SEARCH.EMPTY_DESCRIPTION' | translate }}

View File

@ -4,7 +4,7 @@ div.summary
div.data
span.number(ng-bind="stats.completedPercentage + '%'")
div.summary-stats
div.summary-stats(ng-if="stats.total_points")
span.number(ng-bind="stats.total_points") --
span.description(translate="BACKLOG.SUMMARY.PROJECT_POINTS")
div.summary-stats
@ -17,6 +17,9 @@ div.summary
span.number(ng-bind="stats.speed | number:0") --
span.description(translate="BACKLOG.SUMMARY.POINTS_PER_SPRINT")
div.stats.js-toggle-burndown-visibility-button(title="{{'BACKLOG.SPRINT_SUMMARY.TOGGLE_BAKLOG_GRAPH' | translate}}")
div.stats.js-toggle-burndown-visibility-button(
title="{{'BACKLOG.SPRINT_SUMMARY.TOGGLE_BAKLOG_GRAPH' | translate}}",
ng-if="!showGraphPlaceholder"
)
include ../../../svg/graph.svg

View File

@ -1,4 +1,4 @@
section.list-filters(tg-check-permission="add_issue")
section.issues-options(tg-check-permission="add_issue")
div.new-issue
a.button-green(href="", ng-click="ctrl.addNewIssue()")
span.text(translate="ISSUES.ACTION_NEW_ISSUE")

View File

@ -30,10 +30,10 @@ section.issues-table.basic-table(ng-class="{empty: !issues.length}")
figure.avatar
span.icon.icon-arrow-bottom(tg-check-permission="modify_issue")
section.empty.empty-issues(ng-class="{hidden: issues.length}")
span.icon.icon-issues
span.title(translate="ISSUES.TABLE.EMPTY.TITLE")
span(translate="ISSUES.TABLE.EMPTY.SUBTITLE")
a(href="", ng-click="ctrl.addNewIssue()",
title="{{'ISSUES.TABLE.EMPTY.ACTION_CREATE_ISSUE' | translate}}",
translate="ISSUES.TABLE.EMPTY.ACTION_CREATE_ISSUE")
section.empty-issues(ng-if="!issues.length")
img(
src="../../images/issues-empty.png",
alt="{{ISSUES.TABLE.EMPTY.TITLE | translate }}"
)
p.title(translate="ISSUES.TABLE.EMPTY.TITLE")
p(translate="ISSUES.TABLE.EMPTY.SUBTITLE")

View File

@ -40,15 +40,19 @@ div.kanban-table(tg-kanban-squish-column)
div.kanban-table-body
div.kanban-table-inner
div.kanban-uses-box.task-column(ng-class='{vfold:folds[s.id]}',
ng-repeat="s in usStatusList track by s.id",
tg-kanban-sortable,
tg-kanban-wip-limit="s",
tg-kanban-column-height-fixer,
tg-bind-scope)
div.kanban-task(ng-repeat="us in usByStatus[s.id] track by us.id",
tg-kanban-userstory, ng-model="us", tg-bind-scope,
tg-class-permission="{'readonly': '!modify_task'}"
ng-class="ctrl.getCardClass(s.id)")
ng-repeat="s in usStatusList track by s.id",
tg-kanban-sortable,
tg-kanban-wip-limit="s",
tg-kanban-column-height-fixer,
tg-bind-scope
)
div.kanban-task(
ng-repeat="us in usByStatus[s.id] track by us.id",
tg-kanban-userstory,
ng-model="us",
tg-bind-scope,
tg-class-permission="{'readonly': '!modify_task'}"
ng-class="{'kanban-task-maximized': ctrl.isMaximized(s.id), 'kanban-task-minimized': ctrl.isMinimized(s.id), 'card-placeholder': us.isPlaceholder}"
placeholder="{{us.isPlaceholder}}"
)
div.kanban-column-intro(ng-if="s.is_archived", tg-kanban-archived-status-intro="s")

View File

@ -18,10 +18,8 @@ script(type="text/ng-template", id="search-issues")
div.status(tg-listitem-issue-status="issue")
div.assigned-to(tg-listitem-assignedto="issue")
div.empty.empty-search-results(ng-class="{'hidden': issues.length}")
span.icon.icon-issues
span.title(translate="SEARCH.EMPTY_TITLE")
span(translate="SEARCH.EMPTY_DESCRIPTION")
div.empty-search-results(ng-class="{'hidden': issues.length}")
include ../components/empty-search-results
script(type="text/ng-template", id="search-userstories")
@ -42,10 +40,8 @@ script(type="text/ng-template", id="search-userstories")
div.status(tg-listitem-us-status="us")
div.points(tg-bo-bind="us.total_points")
div.empty.empty-search-results(ng-class="{'hidden': userstories.length}")
span.icon.icon-issues
span.title(translate="SEARCH.EMPTY_TITLE")
span(translate="SEARCH.EMPTY_DESCRIPTION")
div.empty-search-results(ng-class="{'hidden': userstories.length}")
include ../components/empty-search-results
script(type="text/ng-template", id="search-tasks")
div.search-result-table-container(ng-class="{'hidden': !tasks.length}", tg-bind-scope)
@ -65,10 +61,8 @@ script(type="text/ng-template", id="search-tasks")
div.status(tg-listitem-task-status="task")
div.assigned-to(tg-listitem-assignedto="task")
div.empty.empty-search-results(ng-class="{'hidden': tasks.length}")
span.icon.icon-issues
span.title(translate="SEARCH.EMPTY_TITLE")
span(translate="SEARCH.EMPTY_DESCRIPTION")
div.empty-search-results(ng-class="{'hidden': tasks.length}")
include ../components/empty-search-results
script(type="text/ng-template", id="search-wikipages")
div.search-result-table-container(ng-class="{'hidden': !wikipages.length}", tg-bind-scope)
@ -82,7 +76,5 @@ script(type="text/ng-template", id="search-wikipages")
a(href="", tg-nav="project-wiki-page:project=project.slug,slug=wikipage.slug",
tg-bo-bind="wikipage.slug")
div.empty.empty-search-results(ng-class="{'hidden': wikipages.length}")
span.icon.icon-issues
span.title(translate="SEARCH.EMPTY_TITLE")
span(translate="SEARCH.EMPTY_DESCRIPTION")
div.empty-search-results(ng-class="{'hidden': wikipages.length}")
include ../components/empty-search-results

View File

@ -1,13 +1,32 @@
section.sprints
header
h1(translate="BACKLOG.SPRINTS.TITLE")
div.summary
div.total-sprints
span.number(ng-bind="project.total_milestones") --
span.description(translate="BACKLOG.SPRINTS.NUMBER_SPRINTS")
a.button-green.add-sprint(href="", title="{{ 'BACKLOG.SPRINTS.TITLE_ACTION_NEW_SPRINT' | translate }}",
ng-click="ctrl.addNewSprint()", tg-check-permission="add_milestone")
span.text(translate="BACKLOG.SPRINTS.ACTION_NEW_SPRINT")
header.sprint-header
h1
span.number(
ng-bind="totalMilestones"
ng-if="totalMilestones"
)
span(translate="BACKLOG.SPRINTS.TITLE")
a.add-sprint(
href=""
title="{{ 'BACKLOG.SPRINTS.TITLE_ACTION_NEW_SPRINT' | translate}}"
ng-click="ctrl.addNewSprint()"
ng-if="totalMilestones"
tg-check-permission="add_milestone"
)
include ../../../svg/add.svg
div.sprints-empty(ng-if="!totalMilestones")
img(
src="/images/sprint-empty.png"
alt="{{'BACKLOG.SPRINTS.EMPTY' | translate}}"
)
p.title(translate="BACKLOG.SPRINTS.EMPTY")
a(
href=""
ng-click="ctrl.addNewSprint()"
title="{{'BACKLOG.SPRINTS.TITLE_ACTION_NEW_SPRINT' | translate}}"
translate="BACKLOG.SPRINTS.TEXT_ACTION_NEW_SPRINT"
)
div.sprint.sprint-open(ng-repeat="sprint in openSprints track by sprint.id",
tg-backlog-sprint="sprint",
@ -15,7 +34,7 @@ section.sprints
include sprint
a.filter-closed-sprints(href="", tg-backlog-toggle-closed-sprints-visualization,
ng-show="totalClosedMilestones")
ng-if="totalClosedMilestones")
span.icon.icon-archive
span.text(translate="BACKLOG.SPRINTS.ACTION_SHOW_CLOSED_SPRINTS")

View File

@ -24,11 +24,21 @@ div.taskboard-table(tg-taskboard-squish-column)
span(ng-bind="us.total_points")
span(translate="TASKBOARD.TABLE.FIELD_POINTS")
include ../components/addnewtask
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
div.taskboard-task(ng-repeat="task in usTasks[us.id][st.id] track by task.id",
tg-taskboard-task, tg-bind-scope, tg-class-permission="{'readonly': '!modify_task'}")
div.taskboard-task(
ng-repeat="task in usTasks[us.id][st.id] track by task.id"
tg-bind-scope
tg-class-permission="{'readonly': '!modify_task'}"
ng-class="{'card-placeholder': task.isPlaceholder}"
)
div(ng-if="!task.isPlaceholder", tg-taskboard-task)
include ../components/taskboard-task
div(ng-if="task.isPlaceholder")
- var card = 'task'
include ../../common/components/taskboard-placeholder
div.task-row(ng-init="us = null", ng-class="{'row-fold':usFolded[null]}")
div.taskboard-userstory-box.task-column
a.icon.icon-vfold.vfold(href="", title="{{'TASKBOARD.TABLE.TITLE_ACTION_FOLD_ROW' | translate}}", ng-click='foldUs()', ng-class="{hidden:usFolded[null]}")
@ -37,6 +47,15 @@ div.taskboard-table(tg-taskboard-squish-column)
span(translate="TASKBOARD.TABLE.ROW_UNASSIGED_TASKS_TITLE")
include ../components/addnewtask.jade
div.taskboard-tasks-box.task-column(ng-repeat="st in taskStatusList track by st.id", tg-taskboard-sortable, class="squish-status-{{st.id}}", ng-class="{'column-fold':statusesFolded[st.id]}", tg-bind-scope)
div.taskboard-task(ng-repeat="task in usTasks[null][st.id] track by task.id",
tg-taskboard-task, tg-bind-scope, tg-class-permission="{'readonly': '!modify_task'}")
include ../components/taskboard-task
div.taskboard-task(
ng-repeat="task in usTasks[null][st.id] track by task.id"
tg-taskboard-task
tg-bind-scope
tg-class-permission="{'readonly': '!modify_task'}"
ng-class="{'card-placeholder': task.isPlaceholder}"
)
div(ng-if="!task.isPlaceholder")
include ../components/taskboard-task
div(ng-if="task.isPlaceholder")
include ../../common/components/taskboard-placeholder

View File

@ -9,7 +9,7 @@ div.wrapper.issues(tg-issues, ng-controller="IssuesController as ctrl", ng-init=
header
include ../includes/components/mainTitle
include ../includes/modules/list-filters
include ../includes/modules/issues-options
include ../includes/modules/issues-table
// Paginator is rendered using js.

View File

@ -1,4 +1,8 @@
div.kanban-tagline(tg-colorize-tags="us.tags", tg-colorize-tags-type="kanban", ng-hide="us.isArchived")
div.kanban-tagline(
tg-colorize-tags="us.tags"
tg-colorize-tags-type="kanban"
ng-hide="us.isArchived"
)
div.kanban-task-inner(ng-class="{'task-archived': us.isArchived}")
div.avatar-wrapper(tg-kanban-user-avatar="us.assigned_to", ng-model="us", ng-hide="us.isArchived")
div.task-text(ng-hide="us.isArchived")

View File

@ -10,11 +10,11 @@
padding: .4rem 2.5rem;
text-align: center;
text-transform: uppercase;
transition: all .3s linear;
transition: all .2s linear;
vertical-align: middle;
&:hover {
color: $white;
transition: all .3s linear;
transition: all .2s linear;
}
&:visited {
color: $white;
@ -49,6 +49,9 @@
color: $primary;
}
}
&:visited {
color: $blackish;
}
}

View File

@ -0,0 +1,36 @@
.card-placeholder {
background: darken($whitish, 2%);
border: 1px dashed darken($whitish, 8%);
cursor: default;
padding: 1rem;
.placeholder-avatar {
display: flex;
}
.image {
background: darken($whitish, 8%);
flex-basis: 48px;
height: 48px;
margin-right: .5rem;
width: 48px;
}
.text {
flex: 1;
}
.line {
background: darken($whitish, 8%);
height: 1rem;
margin-bottom: 1rem;
width: 80%;
&:last-child {
width: 40%;
}
}
.title {
text-transform: uppercase;
}
p {
@extend %light;
color: $gray;
margin: 0;
}
}

View File

@ -33,6 +33,11 @@
color: $white;
}
}
&.card-placeholder {
background: darken($whitish, 2%);
border: 3px dashed darken($whitish, 8%);
cursor: default;
}
.kanban-tagline {
border-color: $card-hover;
display: flex;

View File

@ -150,6 +150,49 @@ $summary-background: $grayer;
}
}
.graphics-container {
@include slide(300px, hidden, 0);
.empty-burndown {
@extend %light;
align-content: center;
align-items: center;
background: rgba($primary-dark, .15);
display: flex;
flex-direction: row;
justify-content: center;
margin-bottom: 1rem;
padding: 2rem 6rem;
svg {
flex-basis: 5rem;
flex-shrink: 0;
margin-right: 1rem;
width: 5rem;
}
p {
margin: 0;
}
.empty-text {
flex: 1;
}
.title {
@extend %light;
@extend %large;
color: $primary-dark;
margin: 0;
text-transform: uppercase;
}
path {
fill: $primary-dark;
}
a {
@extend %bold;
animation: blink 2s infinite;
}
}
.graphics-container {
$height: 300px;
@include slide($height, hidden, 0);
&.shown {
max-height: $height;
transition: none;
}
}

View File

@ -36,6 +36,11 @@
}
}
}
&.card-placeholder {
background: darken($whitish, 2%);
border: 3px dashed darken($whitish, 8%);
cursor: default;
}
.taskboard-tagline {
border-color: $card-hover;
display: flex;

View File

@ -58,3 +58,12 @@
transform: translateY(0);
}
}
@keyframes blink {
85% {
opacity: 1;
}
100% {
opacity: .6;
}
}

View File

@ -79,26 +79,3 @@ sup {
}
}
}
//Empty
.empty {
border: 1px dashed $gray-light;
color: $gray-light;
min-height: 10rem;
padding: 5% 0;
text-align: center;
.icon {
@extend %xxlarge;
margin-bottom: 2rem;
}
span {
display: block;
&.title {
@extend %xlarge;
@extend %title;
margin-bottom: 1rem;
text-transform: uppercase;
}
}
}

View File

@ -4,17 +4,24 @@
display: flex;
justify-content: space-between;
margin-bottom: 1rem;
padding: .5rem;
.trans-button {
@extend %trans-button;
color: $blackish;
display: inline-block;
margin-right: 1rem;
padding: .3rem 0;
padding: .4rem 1.5rem;
&.active,
&:hover {
background: $gray;
color: $whitish;
}
&.active {
&:hover {
background: lighten($gray, 30%);
}
}
&.move-to-current-sprint {
display: none;
}
span {
vertical-align: middle;
}
}
.button-bulk {
margin-left: .2rem;

View File

@ -12,7 +12,4 @@
.burndown-container {
display: none;
}
.list-filters {
margin-bottom: 1rem;
}
}

View File

@ -248,3 +248,20 @@
padding-right: 45px;
}
}
.empty-backlog {
@extend %light;
padding: 2rem;
text-align: center;
img {
margin-bottom: 1rem;
}
.title {
@extend %large;
margin-bottom: .5rem;
text-transform: uppercase;
}
a {
color: $primary;
}
}

View File

@ -1,17 +1,27 @@
.sprints {
.summary {
background: darken($whitish, 10%);
.sprint-header {
align-content: center;
align-items: center;
display: flex;
justify-content: space-between;
}
.total-sprints {
align-items: flex-start;
color: $grayer;
display: flex;
h1 {
margin: 0;
}
.add-sprint {
margin: 0;
padding: .3rem 1.5rem;
background: $primary;
padding: .25rem .25rem 0;
transition: background .2s;
&:hover {
background: $primary-light;
}
svg {
height: 1.4rem;
width: 1.5rem;
}
path {
fill: $whitish;
}
}
.filter-closed-sprints {
@extend %small;
@ -231,3 +241,20 @@
background: $gray-light;
}
}
.sprints-empty {
@extend %light;
text-align: center;
img {
margin: 1rem 0;
width: 50%;
}
.title {
@extend %large;
margin-bottom: .5rem;
text-transform: uppercase;
}
a {
color: $primary;
}
}

View File

@ -1,10 +1,9 @@
.list-filters {
.issues-options {
align-items: center;
background-color: $whitish;
display: flex;
justify-content: flex-end;
margin-bottom: 2rem;
padding: .5rem 1rem;
.button-bulk {
margin-left: .2rem;
}

View File

@ -126,4 +126,18 @@
}
}
.empty-issues {
margin-top: 4rem;
text-align: center;
img {
margin-bottom: 1rem;
}
.title {
@extend %large;
text-transform: uppercase;
}
p {
@extend %light;
margin: 0;
}
}

View File

@ -1,10 +1,4 @@
.search-result-table {
.empty {
.title {
border: 0;
}
}
.row {
align-content: center;
align-items: center;
@ -64,14 +58,6 @@
}
}
}
.title {
@extend %medium;
@extend %bold;
border-bottom: 1px solid $gray-light;
&:hover {
background: transparent;
}
}
.table-main {
@extend %small;
border-bottom: 1px solid $whitish;
@ -96,3 +82,19 @@
}
}
}
.empty-search-results {
margin-top: 4rem;
text-align: center;
img {
margin-bottom: 1rem;
}
.title {
@extend %large;
text-transform: uppercase;
}
p {
@extend %light;
margin: 0;
}
}

3
app/svg/add.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" version="1.1">
<path d="m462.1 73.3 0 352.3-352.4 0 0 75.8 352.4 0 0 352.3 75.8 0 0-352.3 352.4 0 0-75.8-352.4 0 0-352.3-75.8 0z"/>
</svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 287.81297 236.94013">
<g transform="translate(-2.9e2 -5.7e2)">
<path class="graph" d="m4.7e2 5.7e2c-4.4 0-7.9 3.3-7.9 7.3v2e2c0 4.1 3.5 7.3 7.9 7.3h45c4.4 0 7.9-3.3 7.9-7.3v-2e2c0-4.1-3.5-7.3-7.9-7.3h-45zm-72 70c-4.4 0-7.9 3.3-7.9 7.3v1.3e2c0 4.1 3.5 7.3 7.9 7.3h45c4.4 0 7.9-3.3 7.9-7.3v-1.3e2c0-4.1-3.5-7.3-7.9-7.3h-45zm-72 62c-4.4 0-7.9 3.3-7.9 7.3v67c0 4.1 3.5 7.3 7.9 7.3h45c4.4 0 7.9-3.3 7.9-7.3v-67c0-4.1-3.5-7.3-7.9-7.3h-45z" />
<rect class="white-line" transform="rotate(-30)" rx="0" ry="0" height="28" width="3.1e2" y="8e2" x="-1.3e2"/>
<rect class="color-line" ry="0" rx="0" transform="rotate(-30)" height="28" width="3.1e2" y="8.3e2" x="-1.3e2"/>
</g>
<svg viewbox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<g transform="rotate(-90 510.712 712.45) scale(89.12866)">
<path d="M3 3h8v2H3zm0 4h10v2H3zm0 4h4v2H3z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 943 B

After

Width:  |  Height:  |  Size: 254 B