From d18767789d1c7000c8d2cb770e7168b89b8e5a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Mon, 9 Apr 2018 16:32:49 +0200 Subject: [PATCH] Due date component --- app/locales/taiga/locale-en.json | 6 ++ .../card/card-templates/card-data.jade | 5 ++ .../components/due-date/due-date-button.jade | 9 +++ .../due-date/due-date-controller.coffee | 72 +++++++++++++++++++ .../components/due-date/due-date-icon.jade | 7 ++ .../due-date/due-date.directive.coffee | 43 +++++++++++ app/modules/components/due-date/due-date.scss | 65 +++++++++++++++++ .../includes/components/backlog-row.jade | 5 ++ app/partials/issue/issues-detail.jade | 8 +++ app/partials/task/related-task-row.jade | 6 +- app/partials/task/task-detail.jade | 8 +++ app/partials/us/us-detail.jade | 8 +++ app/svg/sprite.svg | 4 ++ app/themes/high-contrast/variables.scss | 5 +- app/themes/material-design/variables.scss | 5 +- app/themes/taiga/variables.scss | 5 +- 16 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 app/modules/components/due-date/due-date-button.jade create mode 100644 app/modules/components/due-date/due-date-controller.coffee create mode 100644 app/modules/components/due-date/due-date-icon.jade create mode 100644 app/modules/components/due-date/due-date.directive.coffee create mode 100644 app/modules/components/due-date/due-date.scss diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index fde336a3..632935b6 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -162,6 +162,12 @@ "TITLE_ACTION_EDIT_ASSIGNMENT": "Edit assignment", "SELF": "Assign to me" }, + "DUE_DATE": { + "TITLE_ACTION_SET_DUE_DATE": "Set due date", + "DUE_SOON": "due soon", + "PAST_DUE": "past due", + "NO_LONGER_APPLICABLE": "no longer applicable" + }, "STATUS": { "CLOSED": "Closed", "OPEN": "Open" diff --git a/app/modules/components/card/card-templates/card-data.jade b/app/modules/components/card/card-templates/card-data.jade index 73f941c2..7138f257 100644 --- a/app/modules/components/card/card-templates/card-data.jade +++ b/app/modules/components/card/card-templates/card-data.jade @@ -10,6 +10,11 @@ ng-if="vm.item.getIn(['model', 'total_points'])" ) {{"COMMON.FIELDS.POINTS" | translate}} {{vm.item.getIn(['model', 'total_points'])}} .card-statistics + tg-due-date.statistic.card-due-date( + due-date="vm.item.getIn(['model', 'due_date'])" + due-date-status="vm.item.getIn(['model', 'due_date_status'])" + is-closed="vm.item.getIn(['model', 'is_closed'])" + ) .statistic.card-iocaine( ng-if="vm.item.getIn(['model', 'is_iocaine'])" title="{{'COMMON.IOCAINE_TEXT' | translate}}" diff --git a/app/modules/components/due-date/due-date-button.jade b/app/modules/components/due-date/due-date-button.jade new file mode 100644 index 00000000..20586453 --- /dev/null +++ b/app/modules/components/due-date/due-date-button.jade @@ -0,0 +1,9 @@ +a.due-date-button.button-gray.is-editable( + href="" + ng-if="vm.visible()" + ng-disabled="vm.disabled()" + ng-class="vm.color()" + ng-attr-title="{{ vm.title() }}" + ng-click="vm.setDueDate()" +) + tg-svg(svg-icon="icon-clock") diff --git a/app/modules/components/due-date/due-date-controller.coffee b/app/modules/components/due-date/due-date-controller.coffee new file mode 100644 index 00000000..3d0cff0e --- /dev/null +++ b/app/modules/components/due-date/due-date-controller.coffee @@ -0,0 +1,72 @@ +### +# Copyright (C) 2014-2018 Taiga Agile LLC +# +# 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 . +# +# File: due-date-controller.coffee +### + +class DueDateController + @.$inject = [ + "$translate" + "tgLightboxFactory" + ] + + constructor: (@translate, @tgLightboxFactory) -> + + visible: () -> + return @.format == 'button' or @.dueDate? + + disabled: () -> + return @.isClosed + + color: () -> + colors = { + 'no_longer_applicable': 'closed', + 'due_soon': 'due-soon', + 'past_due': 'past-due', + 'set': 'due-set', + } + return colors[@.dueDateStatus] or '' + + title: () -> + if @.format == 'button' + return if @.dueDate then @._formatTitle() else 'EDIT DUE DATE' + + return @._formatTitle() + + _formatTitle: () -> + dueDateStatus = 'closed' + 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) + + if not titles[@.dueDateStatus] + return formatedDate + return "#{formatedDate} (#{@translate.instant(titles[@.dueDateStatus])})" + + setDueDate: () -> + event.preventDefault() + return if @.disabled() + @tgLightboxFactory.create( + "tg-lb-set-due-date", + {"class": "lightbox lightbox-set-due-date"}, + {"object": @.item} + ) + +angular.module('taigaComponents').controller('DueDate', DueDateController) diff --git a/app/modules/components/due-date/due-date-icon.jade b/app/modules/components/due-date/due-date-icon.jade new file mode 100644 index 00000000..16d2b20c --- /dev/null +++ b/app/modules/components/due-date/due-date-icon.jade @@ -0,0 +1,7 @@ +span.due-date-icon + tg-svg( + ng-if="vm.visible()" + svg-icon="icon-clock" + ng-class="vm.color()" + ng-attr-title="{{ vm.title() }}" + ) \ No newline at end of file diff --git a/app/modules/components/due-date/due-date.directive.coffee b/app/modules/components/due-date/due-date.directive.coffee new file mode 100644 index 00000000..29a9a1eb --- /dev/null +++ b/app/modules/components/due-date/due-date.directive.coffee @@ -0,0 +1,43 @@ +### +# Copyright (C) 2014-2018 Taiga Agile LLC +# +# 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 . +# +# File: due-date.directive.coffee +### + +module = angular.module("taigaComponents") + +dueDateDirective = () -> + templateUrl = (el, attrs) -> + if attrs.format + return "components/due-date/due-date-" + attrs.format + ".html" + return "components/due-date/due-date-icon.html" + + return { + link: (scope) -> + controller: "DueDate", + controllerAs: "vm", + bindToController: true, + templateUrl: templateUrl, + scope: { + dueDate: '=', + dueDateStatus: '=', + isClosed: '=', + item: '=', + format: '@' + } + } + +module.directive('tgDueDate', dueDateDirective) \ No newline at end of file diff --git a/app/modules/components/due-date/due-date.scss b/app/modules/components/due-date/due-date.scss new file mode 100644 index 00000000..d282b2c8 --- /dev/null +++ b/app/modules/components/due-date/due-date.scss @@ -0,0 +1,65 @@ +tg-due-date .due-date-button { + background: $gray-light; + display: inline-block; + margin-right: .5rem; + padding: 1rem; + transition: background .2s linear; + transition-delay: .1s; + &.closed, + &.closed[disabled] { + background: $gray-lighter; + } + &.due-set { + background: $yellow-green; + } + &.due-soon { + background: $my-sin; + } + &.past-due { + background: $red-light; + } + &:hover { + background: $gray; + } + &.editable { + cursor: pointer; + } +} + +tg-due-date .due-date-icon { + display: inline-block; + line-height: .1rem; + margin: 0 .25rem; + position: relative; + top: .1rem; + svg { + fill: $gray-light; + height: 1.1rem; + transition: fill .2s ease-in; + width: 1.1rem; + } + .closed svg { + fill: $gray-lighter; + } + .due-set svg { + fill: $yellow-green; + } + .due-soon svg { + fill: $my-sin; + } + .past-due svg { + fill: $red-light; + } +} + +.backlog-table-body .user-story-name .due-date-icon { + top: .25rem; +} + +.card-statistics .due-date-icon { + margin: 0; + svg { + height: 1rem; + width: 1rem; + } +} diff --git a/app/partials/includes/components/backlog-row.jade b/app/partials/includes/components/backlog-row.jade index 10af8afe..42764666 100644 --- a/app/partials/includes/components/backlog-row.jade +++ b/app/partials/includes/components/backlog-row.jade @@ -27,6 +27,11 @@ ) span(tg-bo-ref="us.ref") span(ng-bind-html="us.subject | emojify") + tg-due-date( + due-date="us.due_date" + due-date-status="us.due_date_status" + ng-if="us.due_date" + ) tg-belong-to-epics( format="pill" ng-if="us.epics" diff --git a/app/partials/issue/issues-detail.jade b/app/partials/issue/issues-detail.jade index 0b0d15a3..cc292a2d 100644 --- a/app/partials/issue/issues-detail.jade +++ b/app/partials/issue/issues-detail.jade @@ -113,6 +113,14 @@ div.wrapper( ) section.ticket-detail-settings + tg-due-date( + tg-check-permission="modify_issue" + due-date="issue.due_date" + due-date-status="issue.due_date_status" + is-closed="issue.is_closed" + item="issue" + format="button" + ) tg-promote-issue-to-us-button( tg-check-permission="add_us", ng-model="issue" diff --git a/app/partials/task/related-task-row.jade b/app/partials/task/related-task-row.jade index cde848b1..32ee05b9 100644 --- a/app/partials/task/related-task-row.jade +++ b/app/partials/task/related-task-row.jade @@ -3,7 +3,11 @@ tg-nav="project-tasks-detail:project=project.slug,ref=task.ref") span #<%- task.ref %> span(ng-non-bindable) <%= emojify(task.subject) %> - + tg-due-date( + due-date="task.due_date" + due-date-status="task.due_date_status" + ng-if="task.due_date" + ) .task-settings <% if(perms.modify_task) { %> a.edit-task( diff --git a/app/partials/task/task-detail.jade b/app/partials/task/task-detail.jade index 3cc5939d..3d1e6357 100644 --- a/app/partials/task/task-detail.jade +++ b/app/partials/task/task-detail.jade @@ -102,6 +102,14 @@ div.wrapper( ) section.ticket-detail-settings + tg-due-date( + tg-check-permission="modify_task" + due-date="task.due_date" + due-date-status="task.due_date_status" + is-closed="task.is_closed" + item="task" + format="button" + ) tg-task-is-iocaine-button(ng-model="task") tg-block-button(tg-check-permission="modify_task", ng-model="task") tg-delete-button( diff --git a/app/partials/us/us-detail.jade b/app/partials/us/us-detail.jade index be89305b..91bcb657 100644 --- a/app/partials/us/us-detail.jade +++ b/app/partials/us/us-detail.jade @@ -127,6 +127,14 @@ div.wrapper( ) {{'US.TRIBE.PUBLISH_INFO' | translate}} section.ticket-detail-settings + tg-due-date( + tg-check-permission="modify_us" + due-date="us.due_date" + due-date-status="us.due_date_status" + is-closed="us.is_closed" + item="us" + format="button" + ) tg-us-team-requirement-button(ng-model="us") tg-us-client-requirement-button(ng-model="us") tg-block-button( diff --git a/app/svg/sprite.svg b/app/svg/sprite.svg index bdca9a97..8be33777 100644 --- a/app/svg/sprite.svg +++ b/app/svg/sprite.svg @@ -244,6 +244,10 @@ class="path1" d="M202.24-0.179v129.093l-202.24-0.11v895.375h729.6v-234.419h-64v170.419h-601.6v-644.385h601.6v287.086h64v-474.076h-64v0.11h-138.24v-129.093h-325.12zM266.24 63.821h197.12v96.44h0.32v32.653h201.92v58.88h-601.6v-58.88h202.24v-129.093zM129.165 393.242v64h468.838v-64h-468.836zM522.76 515.302l-181.020 181.018 181.020 181.020 45.256-45.253-103.759-103.767h559.744v-64h-559.749l103.764-103.764-45.253-45.256zM129.165 541.722v64h228.086v-64h-228.083zM129.165 690.202v64h150.246v-64h-150.246zM129.165 833.562v64h258.854v-64h-258.854z"> + + clock + + document