Display user-defined due dates colors

stable
Daniel García 2018-06-06 13:50:59 +02:00 committed by Alex Hermida
parent b90f70d846
commit 1e56104e58
29 changed files with 410 additions and 67 deletions

View File

@ -113,7 +113,7 @@ class ProjectValuesController extends taiga.Controller
unwatch()
loadValues: =>
return @rs[@scope.resource].listValues(@scope.projectId, @scope.type).then (values) =>
@scope.values = values
if values.length
@scope.maxValueOrder = _.maxBy(values, "order").order
return values
@ -130,6 +130,46 @@ class ProjectValuesController extends taiga.Controller
module.controller("ProjectValuesController", ProjectValuesController)
#############################################################################
## Project due dates values Controller
#############################################################################
class ProjectDueDatesValuesController extends ProjectValuesController
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgConfirm",
"$tgResources",
]
loadValues: =>
return @rs[@scope.resource].listValues(@scope.projectId, @scope.type).then (values) =>
if values.length
@scope.maxValueOrder = _.maxBy(values, "order").order
@displayValues(values)
else
@createDefaultValues()
return values
createDefaultValues: =>
if !@rs[@scope.resource].createDefaultValues?
return
return @rs[@scope.resource].createDefaultValues(@scope.projectId, @scope.type).then (response) =>
values = response.data
if values.length
@scope.maxValueOrder = _.maxBy(values, "order").order
@displayValues(values)
return values
displayValues: (values) =>
_.each values, (value, index) ->
value.days_to_due_abs = if value.days_to_due != null then Math.abs(value.days_to_due) else null
value.sign = if value.days_to_due >= 0 then 1 else -1
@scope.values = values
module.controller("ProjectDueDatesValuesController", ProjectDueDatesValuesController)
#############################################################################
## Project values directive
#############################################################################
@ -288,6 +328,11 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
target = angular.element(event.currentTarget)
saveValue(target)
$el.on "click", ".save", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
saveValue(target)
$el.on "click", ".cancel", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
@ -332,6 +377,45 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
module.directive("tgProjectValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame",
"$translate", "$rootScope", "tgProjectService", ProjectValuesDirective])
#############################################################################
## Project due dates values directive
#############################################################################
ProjectDueDatesValues = ($log, $repo, $confirm, $location, animationFrame, $translate, $rootscope, projectService) ->
parentDirective = ProjectValuesDirective($log, $repo, $confirm, $location, animationFrame,
$translate, $rootscope, projectService)
linkDueDateStatusValue = ($scope, $el, $attrs) ->
_setDaysToDue = (value) ->
value.days_to_due = value.days_to_due_abs * value.sign
_valueFromEventTarget = (event) ->
target = angular.element(event.currentTarget)
row = target.parents(".row.table-main")
formEl = target.parents("form")
return formEl.scope().value
$el.on "input", ".days-to-due-abs", (event) ->
event.preventDefault()
value = _valueFromEventTarget(event)
$scope.$apply ->
_setDaysToDue(value)
$el.on "click", ".days-to-due-sign", (event) ->
event.preventDefault()
value = _valueFromEventTarget(event)
$scope.$apply ->
value.sign = value.sign * -1
_setDaysToDue(value)
return {
link: ($scope, $el, $attrs) ->
parentDirective.link($scope, $el, $attrs)
linkDueDateStatusValue($scope, $el, $attrs)
}
module.directive("tgProjectDueDatesValues", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "animationFrame",
"$translate", "$rootScope", "tgProjectService", ProjectDueDatesValues])
#############################################################################
## Color selection directive

View File

@ -376,7 +376,7 @@ module.directive("tgLightboxClose", [LightboxClose])
Svg = () ->
template = """
<svg class="{{ 'icon ' + svgIcon }}">
<svg class="{{ 'icon ' + svgIcon }}" style="fill: {{ svgFill }}">
<use xlink:href="" ng-attr-xlink:href="{{ '#' + svgIcon }}">
<title ng-if="svgTitle">{{svgTitle}}</title>
<title ng-if="svgTitleTranslate">{{svgTitleTranslate | translate: svgTitleTranslateValues}}</title>
@ -389,7 +389,8 @@ Svg = () ->
svgIcon: "@",
svgTitle: "@",
svgTitleTranslate: "@",
svgTitleTranslateValues: "="
svgTitleTranslateValues: "=",
svgFill: "="
},
template: template
}

View File

@ -82,15 +82,18 @@ urls = {
"project-transfer-request": "/projects/%s/transfer_request"
"project-transfer-start": "/projects/%s/transfer_start"
# Project Values - Choises
# Project Values - Attributes
"epic-statuses": "/epic-statuses"
"userstory-statuses": "/userstory-statuses"
"userstory-due-date-statuses": "/userstory-duedates"
"userstory-due-dates": "/userstory-due-dates"
"userstory-due-dates-create-default": "/userstory-due-dates/create_default"
"points": "/points"
"task-statuses": "/task-statuses"
"task-due-date-statuses": "/task-due-date-statuses"
"task-due-dates": "/task-due-dates"
"task-due-dates-create-default": "/task-due-dates/create_default"
"issue-statuses": "/issue-statuses"
"issue-due-date-statuses": "/issue-due-date-statuses"
"issue-due-dates": "/issue-due-dates"
"issue-due-dates-create-default": "/issue-due-dates/create_default"
"issue-types": "/issue-types"
"priorities": "/priorities"
"severities": "/severities"

View File

@ -89,6 +89,11 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
service.storeQueryParams(projectId, params)
return $repo.queryMany(type, params)
service.createDefaultValues = (projectId, type) ->
data = {"project_id": projectId}
url = $urls.resolve("#{type}-create-default")
return $http.post(url, data)
service.storeQueryParams = (projectId, params) ->
ns = "#{projectId}:#{hashSuffix}"
hash = generateHash([projectId, ns])

View File

@ -94,6 +94,11 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
params = {"project": projectId}
return $repo.queryMany(type, params)
service.createDefaultValues = (projectId, type) ->
data = {"project_id": projectId}
url = $urls.resolve("#{type}-create-default")
return $http.post(url, data)
service.storeQueryParams = (projectId, params) ->
ns = "#{projectId}:#{hashSuffix}"
hash = generateHash([projectId, ns])

View File

@ -115,9 +115,13 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
service.listValues = (projectId, type) ->
params = {"project": projectId}
service.storeQueryParams(projectId, params)
console.log type
return $repo.queryMany(type, params)
service.createDefaultValues = (projectId, type) ->
data = {"project_id": projectId}
url = $urls.resolve("#{type}-create-default")
return $http.post(url, data)
service.storeQueryParams = (projectId, params) ->
ns = "#{projectId}:#{hashSuffix}"
hash = generateHash([projectId, ns])

View File

@ -620,11 +620,16 @@
"SELECTED": "Selected"
},
"PROJECT_DUE_DATE_STATUS": {
"TITLE": "Due dates statuses",
"SUBTITLE": "Specify the due date statuses your user stories, tasks and issues will go through",
"US_TITLE": "User Story Due Date Statuses",
"TASK_TITLE": "Task Due Date Statuses",
"ISSUE_TITLE": "Issue Due Date Statuses"
"TITLE": "Due Dates",
"SUBTITLE": "Specify the due dates your user stories, tasks and issues will go through if selected",
"US_TITLE": "User Story Due Date status",
"ACTION_ADD_STATUS": "Add new status",
"TASK_TITLE": "Task Due Date status",
"ISSUE_TITLE": "Issue Due Date status",
"DAYS_TO_DUE_DATE": "Days to due date",
"BEFORE_AFTER": "Before/after",
"BEFORE": "Before",
"AFTER": "After"
},
"ROLES": {
"PAGE_TITLE": "Roles - {{projectName}}",
@ -726,7 +731,8 @@
"LABEL_SEVERITY": "Default value for severity selector"
},
"STATUS": {
"PLACEHOLDER_WRITE_STATUS_NAME": "Write a name for the new status"
"PLACEHOLDER_WRITE_STATUS_NAME": "Write a name for the new status",
"PLACEHOLDER_DAYS_TO_DUE_DATE": "Write a number of days to due date"
},
"TYPES": {
"PLACEHOLDER_WRITE_NAME": "Write a name for the new element"

View File

@ -21,6 +21,7 @@
tg-due-date.statistic.card-due-date(
due-date="vm.item.getIn(['model', 'due_date'])"
is-closed="vm.item.getIn(['model', 'is_closed'])"
obj-type="task"
)
.statistic.card-iocaine(
ng-if="vm.item.getIn(['model', 'is_iocaine'])"

View File

@ -32,6 +32,11 @@ class StoryHeaderController
@.editMode = false
@.loadingSubject = false
@.originalSubject = @.item.subject
@.objType = {
'tasks': 'task',
'issues': 'issue',
'userstories': 'us',
}[@.item._name]
_checkNav: () ->
if @.item.neighbors.previous?.ref?

View File

@ -23,6 +23,7 @@
due-date="vm.item.due_date"
is-closed="vm.item.is_closed"
ng-if="vm.item.due_date"
obj-type="{{ vm.objType }}"
)
.edit-title-wrapper(ng-if="vm.editMode")
input.edit-title-input.e2e-title-input(

View File

@ -1,7 +1,7 @@
label.due-date-button.button-gray.is-editable(
ng-if="vm.visible()"
ng-disabled="vm.disabled()"
ng-class="vm.color()"
ng-style="{background: vm.color()}"
ng-attr-title="{{ vm.title() }}"
ng-click="vm.setDueDate()"
)

View File

@ -22,9 +22,15 @@ class DueDateController
"$translate"
"tgLightboxFactory"
"tgProjectService"
"$rootScope"
]
constructor: (@translate, @tgLightboxFactory, @projectService) ->
constructor: (@translate, @tgLightboxFactory, @projectService, @rootscope) ->
@.defaultConfig = [
{"color": "#9dce0a", "name": "normal due", "days_to_due": null, "by_default": true},
{"color": "#ff9900", "name": "due soon", "days_to_due": 14, "by_default": false},
{"color": "#ff8a84", "name": "past due", "days_to_due": 0, "by_default": false}
]
visible: () ->
return @.format == 'button' or @.dueDate?
@ -33,14 +39,7 @@ class DueDateController
return @.isClosed
color: () ->
colors = {
'no_longer_applicable': 'closed',
'due_soon': 'due-soon',
'past_due': 'past-due',
'set': 'due-set',
'not_set': 'not-set',
}
return colors[@status()] or ''
return @.getStatus()?.color || null
title: () ->
if @.dueDate
@ -49,36 +48,48 @@ class DueDateController
return @translate.instant('COMMON.DUE_DATE.TITLE_ACTION_SET_DUE_DATE')
return ''
status: () ->
getStatus: (options) ->
if !@.dueDate
return 'not_set'
return null
project = @projectService.project.toJS()
project.due_soon_threshold = 14 # TODO get value from taiga-back
options = project["#{@.objType}_duedates"]
if !options
options = @.defaultConfig
return @._getAppearance(options)
_getDefaultAppearance: (options) ->
defaultAppearance = null
_.map options, (option) ->
if option.by_default == true
defaultAppearance = option
return defaultAppearance
_getAppearance: (options) ->
currentAppearance = @._getDefaultAppearance(options)
options = _.sortBy(options, (o) -> - o.days_to_due) # sort desc
dueDate = moment(@.dueDate)
now = moment()
_.map options, (appearance) ->
if appearance.days_to_due == null
return
limitDate = moment(dueDate - moment.duration(appearance.days_to_due, "days"))
if now >= limitDate
currentAppearance = appearance
if @.isClosed
return 'no_longer_applicable'
else if now > dueDate
return 'past_due'
else if now.add(moment.duration(project.due_soon_threshold, "days")) >= dueDate
return 'due_soon'
return 'set'
return currentAppearance
_formatTitle: () ->
titles = {
'no_longer_applicable': 'COMMON.DUE_DATE.NO_LONGER_APPLICABLE',
'due_soon': 'COMMON.DUE_DATE.DUE_SOON',
'past_due': 'COMMON.DUE_DATE.PAST_DUE',
}
prettyDate = @translate.instant("COMMON.PICKERDATE.FORMAT")
formatedDate = moment(@.dueDate).format(prettyDate)
status = @status()
if not titles[status]
status = @.getStatus()
if status?.name
return "#{formatedDate} (#{status.name})"
return formatedDate
return "#{formatedDate} (#{@translate.instant(titles[status])})"
setDueDate: () ->
return if @.disabled()

View File

@ -0,0 +1,130 @@
###
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# File: color-selector.controller.spec.coffee
###
describe "DueDate", ->
provide = null
controller = null
ctrl = null
mocks = {}
dueDateConfig = {
'us': [
{"color": "#ff0000", "name": "past due", "days_to_due": 0, "by_default": false},
{"color": "#00ff00", "name": "normal due", "days_to_due": null, "by_default": true}
{"color": "#333333", "name": "distant past due", "days_to_due": -7, "by_default": false}
{"color": "#ffff00", "name": "due soon", "days_to_due": 14, "by_default": false},
],
'task': [
{"color": "#ff0000", "name": "past due", "days_to_due": 0, "by_default": false},
{"color": "#00ff00", "name": "normal due", "days_to_due": null, "by_default": true}
{"color": "#ffff00", "name": "due soon", "days_to_due": 3, "by_default": false}
],
'issue': [
{"color": "#550000", "name": "past due", "days_to_due": 0, "by_default": false},
{"color": "#00ff00", "name": "normal due", "days_to_due": null, "by_default": true},
{"color": "#AAAA00", "name": "due soon", "days_to_due": 7, "by_default": false},
{"color": "#333333", "name": "distant past due", "days_to_due": -10, "by_default": false}
]
}
_mockTranslate = () ->
mocks.$translate = {
instant: (index) ->
if index == "COMMON.PICKERDATE.FORMAT"
return "DD MMM YYYY"
return 'Set due date'
}
provide.value "$translate", mocks.$translate
_mockTgProjectService = () ->
mocks.tgProjectService = {
project: {
toJS: () -> {}
}
}
provide.value "tgProjectService", mocks.tgProjectService
_mockTgLightboxFactory = () ->
mocks.tgLightboxFactory = {}
provide.value "tgLightboxFactory", mocks.tgLightboxFactory
_mockLog = () ->
mocks.log = {
error: (msg) -> return msg
}
provide.value "$log", mocks.log
_mocks = () ->
module ($provide) ->
provide = $provide
_mockTranslate()
_mockTgLightboxFactory()
_mockTgProjectService()
_mockLog()
return null
_inject = ->
inject ($controller) ->
controller = $controller
beforeEach ->
module "taigaComponents"
_mocks()
_inject()
describe "when is not set", ->
beforeEach ->
ctrl = controller "DueDateCtrl"
ctrl.dueDate = null
ctrl.format = 'button'
it "get title", () ->
expect(ctrl.title()).to.be.eql('Set due date')
it "get color", () ->
expect(ctrl.color()).to.be.eql(null)
describe "when is set", ->
normalDue = ['normal due', '#9dce0a']
dueSoon = ['due soon', '#ff8a84']
pastDue = ['past due', '#ff9900']
runs = [
{ objType: 'us', days: -1, expect: pastDue },
{ objType: 'us', days: 15, expect: normalDue },
{ objType: 'us', days: 2, expect: dueSoon },
{ objType: 'task', days: -3, expect: pastDue },
{ objType: 'task', days: 18, expect: normalDue },
{ objType: 'task', days: 5, expect: dueSoon },
{ objType: 'issue', days: -5, expect: pastDue },
{ objType: 'issue', days: 20, expect: normalDue },
{ objType: 'issue', days: 8, expect: dueSoon },
]
beforeEach ->
ctrl = controller "DueDateCtrl"
ctrl.format = 'button'
runs.forEach (run) ->
it "get appearance in #{run.objType} view with #{run.days} days left", () ->
ctrl.objType = run.objType
ctrl.dueDate = moment().add(moment.duration(run.days, "days"))
formatedDate = ctrl.dueDate.format('DD MMM YYYY')
expect(ctrl.title()).to.be.eql("#{formatedDate} (#{run.expect[0]})")
expect(ctrl.color()).to.be.eql(run.expect[1])

View File

@ -1,6 +1,6 @@
tg-svg.due-date-icon(
ng-if="vm.visible()"
svg-icon="icon-clock"
ng-class="vm.color()"
svg-fill="vm.color()"
ng-attr-title="{{ vm.title() }}"
)

View File

@ -62,6 +62,7 @@ dueDatePopoverDirective = ($translate, datePickerConfigService) ->
dueDate: '=',
isClosed: '=',
item: '=',
objType: '@',
format: '@',
notAutoSave: '='
}

View File

@ -1,7 +1,7 @@
.due-date-button-wrapper
label.due-date-button.button-gray.is-editable.date-picker-popover-trigger(
ng-disabled="vm.disabled()"
ng-class="vm.color()"
ng-style="{background: vm.color()}"
ng-attr-title="{{ vm.title() }}"
)
tg-svg(svg-icon="icon-clock")

View File

@ -70,6 +70,7 @@ dueDateDirective = ($translate, datePickerConfigService) ->
dueDate: '=',
isClosed: '=',
item: '=',
objType: '@',
format: '@',
notAutoSave: '='
}

View File

@ -15,20 +15,20 @@ div.wrapper(ng-controller="ProjectValuesSectionController",
include ../includes/components/mainTitle
p.admin-subtitle(translate="ADMIN.PROJECT_DUE_DATE_STATUS.SUBTITLE")
div.admin-attributes-section(tg-project-values, type="userstory-due-date-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='userstory-due-date-statuses'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.US_TITLE'",
div.admin-attributes-section(tg-project-due-dates-values, type="userstory-due-dates",
ng-controller="ProjectDueDatesValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='userstory-due-dates'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.US_TITLE'",
objName="status")
include ../includes/modules/admin/project-due-date-status
div.admin-attributes-section(tg-project-values, type="task-due-date-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='tasks'; type='task-due-date-statuses'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.TASK_TITLE'"
div.admin-attributes-section(tg-project-due-dates-values, type="task-due-dates",
ng-controller="ProjectDueDatesValuesController as ctrl",
ng-init="section='admin'; resource='tasks'; type='task-due-dates'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.TASK_TITLE'"
objName="status")
include ../includes/modules/admin/project-due-date-status
div.admin-attributes-section(tg-project-values, type="issue-due-date-statuses",
ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='issue-due-date-statuses'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.ISSUE_TITLE'",
div.admin-attributes-section(tg-project-due-dates-values, type="issue-due-dates",
ng-controller="ProjectDueDatesValuesController as ctrl",
ng-init="section='admin'; resource='issues'; type='issue-due-dates'; sectionName='ADMIN.PROJECT_DUE_DATE_STATUS.ISSUE_TITLE'",
objName="status")
include ../includes/modules/admin/project-due-date-status

View File

@ -22,6 +22,7 @@ div.ticket-detail-settings
due-date="obj.due_date"
is-closed="obj.is_closed"
item="obj"
obj-type="issue"
not-auto-save="true"
)
div

View File

@ -8,6 +8,7 @@ div.ticket-detail-settings
due-date="obj.due_date"
is-closed="obj.is_closed"
item="obj"
obj-type="task"
not-auto-save="true"
)
div

View File

@ -11,6 +11,7 @@ div.ticket-detail-settings
due-date="obj.due_date"
is-closed="obj.is_closed"
item="obj"
obj-type="task"
not-auto-save="true"
)
div

View File

@ -31,6 +31,7 @@
due-date="us.due_date"
is-closed="us.is_closed"
ng-if="us.due_date"
obj-type="us"
)
tg-belong-to-epics(
format="pill"

View File

@ -1,15 +1,15 @@
section.colors-table.admin-status-table
div.project-values-title
h2 {{ sectionName | translate }}
a.button.button-gray.show-add-new(href="", title="{{'ADMIN.US_STATUS.ACTION_ADD_STATUS' | translate}}")
span(translate="ADMIN.US_STATUS.ACTION_ADD_STATUS")
a.button.button-gray.show-add-new(href="", title="{{'ADMIN.PROJECT_DUE_DATE_STATUS.ACTION_ADD_STATUS' | translate}}")
span(translate="ADMIN.PROJECT_DUE_DATE_STATUS.ACTION_ADD_STATUS")
div.table-header
div.row
div.color-column(translate="COMMON.FIELDS.COLOR")
div.status-name(translate="COMMON.FIELDS.NAME")
div.status-slug(translate="COMMON.FIELDS.SLUG")
div.thresold-column(translate="COMMON.FIELDS.THRESHOLD")
div.thresold-column(translate="ADMIN.PROJECT_DUE_DATE_STATUS.DAYS_TO_DUE_DATE")
div.before-after-column(translate="ADMIN.PROJECT_DUE_DATE_STATUS.BEFORE_AFTER")
div.options-column
div.table-main
@ -24,11 +24,13 @@ section.colors-table.admin-status-table
div.status-name
span {{ value.name }}
div.status-slug
span {{ value.slug }}
div.thresold-column
span(ng-if="value.days_to_due_abs != null") {{ value.days_to_due_abs }}
div.is-closed-column
span {{ value.days_to_due }}
div.before-after-column
span(ng-if="value.days_to_due_abs", ng-switch="value.days_to_due >= 0")
span(ng-switch-when="true") {{ 'ADMIN.PROJECT_DUE_DATE_STATUS.BEFORE' | translate }}
span(ng-switch-when="false") {{ 'ADMIN.PROJECT_DUE_DATE_STATUS.AFTER' | translate }}
div.options-column
a.edit-value(href="")
@ -36,7 +38,7 @@ section.colors-table.admin-status-table
title="{{'ADMIN.COMMON.TITLE_ACTION_EDIT_VALUE' | translate}}",
svg-icon="icon-edit"
)
a.delete-value(href="")
a.delete-value(href="", ng-if="!value.by_default")
tg-svg(
title="{{'ADMIN.COMMON.TITLE_ACTION_DELETE_VALUE' | translate}}"
svg-icon="icon-trash"
@ -60,8 +62,33 @@ section.colors-table.admin-status-table
data-maxlength="255"
)
div.thresold-column
input(type="number", name="days_to_due", ng-model="value.days_to_due")
div.thresold-column(ng-if="!value.by_default")
input.days-to-due-abs(type="number", min="0", name="days_to_due_abs", ng-model="value.days_to_due_abs")
div.before-after-column
div.before-after-selector(ng-if="!value.by_default && value.days_to_due_abs != 0")
.before-after-selector-single(ng-class="{'checked': value.sign > 0 }")
input(
type='radio'
id="due-date-status-before"
name="due-date-status-after"
ng-model="value.sign"
value="1"
)
label.days-to-due-sign(
for="due-date-status-before",
) {{ 'ADMIN.PROJECT_DUE_DATE_STATUS.BEFORE' | translate }}
.before-after-selector-single(ng-class="{'checked': value.sign < 0 }")
input(
type='radio'
id="due-date-status-after"
name="due-date-status-after"
ng-model="value.sign"
value="-1"
)
label.days-to-due-sign(
for="due-date-status-after",
) {{ 'ADMIN.PROJECT_DUE_DATE_STATUS.AFTER' | translate }}
div.options-column
a.save.e2e-save(href="", title="{{'COMMON.SAVE' | translate}}")
@ -90,7 +117,13 @@ section.colors-table.admin-status-table
)
div.thresold-column
input(type="number")
input(
name="days_to_due"
type="number"
placeholder="{{'ADMIN.STATUS.PLACEHOLDER_DAYS_TO_DUE_DATE' | translate}}"
ng-model="newValue.days_to_due"
data-required="true"
)
div.options-column
a.add-new.e2e-save(href="", title="{{'COMMON.ADD' | translate}}")

View File

@ -58,6 +58,7 @@ section.issues-table.basic-table(ng-class="{empty: !issues.length}")
due-date="issue.due_date"
is-closed="us.is_closed"
ng-if="issue.due_date"
obj-type="issue"
)

View File

@ -127,6 +127,7 @@ div.wrapper(
format="button"
is-closed="issue.is_closed"
item="issue"
obj-type="issue"
tg-check-permission="modify_issue"
)
tg-promote-issue-to-us-button(

View File

@ -5,6 +5,7 @@
span(ng-non-bindable) <%= emojify(task.subject) %>
tg-due-date(
due-date="task.due_date"
obj-type="task"
ng-if="task.due_date"
)
.task-settings

View File

@ -107,6 +107,7 @@ div.wrapper(
format="button"
is-closed="task.is_closed"
item="task"
obj-type="task"
tg-check-permission="modify_task"
)
tg-task-is-iocaine-button(ng-model="task")

View File

@ -132,6 +132,7 @@ div.wrapper(
format="button"
is-closed="us.is_closed"
item="us"
obj-type="us"
tg-check-permission="modify_us"
)
tg-us-team-requirement-button(ng-model="us")

View File

@ -58,11 +58,17 @@
.is-archived-column,
.is-closed-column,
.options-column,
.thresold-column,
.before-after-column,
.status-wip-limit {
flex-basis: 100px;
flex-grow: 1;
flex-shrink: 0;
}
.before-after-column {
padding: 0 10px;
}
.status-name,
.color-name {
flex-basis: 150px;
@ -115,6 +121,43 @@
}
}
.before-after-selector {
display: flex;
font-size: .9rem;
input {
display: none;
+label {
background: rgba($whitish, .7);
cursor: pointer;
display: block;
padding: .5rem .25rem;
text-align: center;
text-transform: uppercase;
transition: background .2s ease-in;
}
+label:hover {
background: rgba($primary-light, .3);
transition: background .2s ease-in;
}
}
.before-after-selector-single {
flex: 1;
margin-right: .25rem;
&:last-child {
margin-right: 0;
}
&.checked label {
background: $primary-light;
color: $white;
transition: background .2s ease-in;
}
&.checked label:hover {
background: $primary-light;
}
}
}
.options-column {
a {
cursor: pointer;