diff --git a/.travis.yml b/.travis.yml index 62f15439..13337fbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ -sudo: false language: node_js node_js: - "4.1" diff --git a/AUTHORS.rst b/AUTHORS.rst index 89ed65b2..31e3c168 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,6 +1,6 @@ The PRIMARY AUTHORS are: -- Andrey Antukh +- Andrey Antukh - Jesus Espino Garcia - David Barragán Merino - Xavi Julian @@ -16,16 +16,17 @@ And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, added translations, helped answer newbie questions, and generally made Taiga that much better: -- Pilar Esteban -- Guilhem Got -- Ramiro Sánchez -- Miguel de la Cruz +- Allister Antosik - Andrea Stagi -- Jordan Rinke -- Wil Wade +- Brett Profitt +- Chris Wilson - Daniel Koch - Florian Bezagu +- Guilhem Got +- Jordan Rinke +- Miguel de la Cruz +- Pilar Esteban +- Ramiro Sánchez - Ryan Swanstrom -- Chris Wilson -- Brett Profitt - Vlad Topala +- Wil Wade diff --git a/CHANGELOG.md b/CHANGELOG.md index 903aba99..4adf2c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,38 @@ # Changelog # +## 1.10.0 Dryas Octopetala (2016-01-30) + +### Features +- New design for the detail pages slidebar. +- Added 'Assign to me' button in User Stories, Tasks and Issues detail pages. (thanks to [@allistera](https://github.com/allistera)). +- Attachments: + - Upload attachments on US/issue/task lightbox. + - Attachments image gallery view mode in detail pages. + - Drag files from desktop to attachments section. + - Drag files from desktop in wysiwyg textareas. +- Project: + - Add a logo to your project. + - Denotes that your project is looking for people and add an explanation. +- Discover section: + - List most liked and most active project (last week/month/year or all time). + - List featured project. + - Search projects: + - Full text search with priorities over title, tags and description fields. + - Order results alphabeticaly, by most liked or more actived. + - Filter by 'use kanban', 'use scrum' or 'looking for people'. +- i18n. + - Add swedish (sv) translation. + - Add turkish (tr) translation. + +### Misc +- Sticky project navigation bar. +- Lots of small and not so small bugfixes. + + ## 1.9.1 Taiga Tribe (2016-01-05) +### Features - [118n] Now taiga plugins can be translatable. - New Taiga plugins system. - Now superadmins can send notifications (live announcement) to the user (through taiga-events). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..fa058ff8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,77 @@ +# Contributing + +#### Where to start #### + +There are many different ways to contribute to Taiga's development, just find the one that best fits with your skills. Examples of contributions we would love to receive include: + +- **Code patches** +- **Documentation improvements** +- **Translations** +- **Bug reports** +- **Patch reviews** +- **UI enhancements** + +Big features are also welcome but if you want to see your contributions included in Taiga codebase we strongly recommend you start by initiating a chat though our [mailing list](http://groups.google.co.uk/d/forum/taigaio) + + +#### Code of Conduct #### + +Help us keep the Taiga Community open and inclusive. Please read and follow our [Code of Conduct](https://github.com/taigaio/code-of-conduct/blob/master/CODE_OF_CONDUCT.md). + + +#### License #### + +Every code patch accepted in taiga codebase is licensed under [AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html). You must be careful to not include any code that can not be licensed under this license. + +Please read carefully [our license](https://github.com/taigaio/taiga-front/blob/master/LICENSE) and ask us if you have any questions. + + +#### Bug reports, enhancements and support #### + +If you **need help to setup Taiga**, want to **talk about some cool enhancemnt** or you have **some questions**, please write us to our [mailing list](http://groups.google.com/d/forum/taigaio). + +If you **find a bug** in Taiga you can always report it: + +- in our [mailing list](http://groups.google.com/d/forum/taigaio). +- in [github issues](https://github.com/taigaio/taiga-front/issues). +- send us a mail to support@taiga.io if is a bug related to [tree.taiga.io](https://tree.taiga.io). +- send a mail to security@taiga.io if is a **security bug**. + +One of our fellow Taiga developers will search, find and hunt it as soon as possible. + +Please, before reporting a bug write down how can we reproduce it, your operating system, your browser and version, and if it's possible, a screenshot. Sometimes it takes less time to fix a bug if the developer knows how to find it and we will solve your problem as fast as possible. + + +#### Documentation improvements #### + +We are gathering lots of information from our users to build and enhance our documentation. If you use the documentation to install or develop with Taiga and find any mistakes, omissions or confused sequences, it is enormously helpful to report it. Or better still, if you believe you can author additions, please make a pull-request to taiga project. + +Currently, we have authored three main documentation hubs: + +- **[API Docs](https://github.com/taigaio/taiga-doc)**: Our API documentation and reference for developing from Taiga API. +- **[Installation Guide](https://github.com/taigaio/taiga-doc)**: If you need to install Taiga on your own server, this is the place to find some guides. +- **[Taiga Support](https://github.com/taigaio/taiga-doc)**: This page is intended to be the support reference page for the users. If you find any mistake, please report it. + + +#### Translation #### + +We are ready now to accept your help translating Taiga. It's easy (and fun!) just access our team of translators with the link below, set up an account in Transifex and start contributing. Join us to make sure your language is covered! **[Help Taiga to translate content](https://www.transifex.com/signup/ "Help Taiga to trasnlatecontent")** + + +#### Code patches #### + +Taiga will always be glad to receive code patches to update, fix or improve its code. + +If you know how to improve our code base or you found a bug, a security vulnerability or a performance issue and you think you can solve it, we will be very happy to accept your pull-request. If your code requires considerable changes, we recommend you first talk to us directly. We will find the best way to help. + + +#### UI enhancements #### + +Taiga is made for developers and designers. We care enormously about UI because usability and design are both critical aspects of the Taiga experience. + +There are two possible ways to contribute to our UI: +- **Bugs**: If you find a bug regarding front-end, please report it as previously indicated in the Bug reports section or send a pull-request as indicated in the Code Patches section. +- **Enhancements**: If its a design or UX bug or enhancement we will love to receive your feedback. Please send us your enhancement, with the reason and, if possible, an example. Our design and UX team will review your enhancement and fix it as soon as possible. We recommend you to use our [mailing list](http://groups.google.co.uk/d/forum/taigaio) so we can have a lot of different opinions and debate. +- **Language Localization**: We are eager to offer localized versions of Taiga. Some members of the community have already volunteered to work to provide a variety of languages. We are working to implement some changes to allow for this and expect to accept these requests in the near future. + + diff --git a/README.md b/README.md index 4c67fc00..dc390dcf 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ There are many different ways to contribute to Taiga's development, just find th Big features are also welcome but if you want to see your contributions included in Taiga codebase we strongly recommend you start by initiating a chat though our [mailing list](http://groups.google.co.uk/d/forum/taigaio) +#### Code of Conduct #### + +Help us keep the Taiga Community open and inclusive. Please read and follow our [Code of Conduct](https://github.com/taigaio/code-of-conduct/blob/master/CODE_OF_CONDUCT.md). + + #### License #### Every code patch accepted in taiga codebase is licensed under [AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html). You must be careful to not include any code that can not be licensed under this license. @@ -43,7 +48,7 @@ If you **find a bug** in Taiga you can always report it: - in our [mailing list](http://groups.google.com/d/forum/taigaio). - in [github issues](https://github.com/taigaio/taiga-front/issues). -- send us a mail to support@taiga.io if is a bug related to tree.taiga.io. +- send us a mail to support@taiga.io if is a bug related to [tree.taiga.io](https://tree.taiga.io). - send a mail to security@taiga.io if is a **security bug**. One of our fellow Taiga developers will search, find and hunt it as soon as possible. @@ -80,7 +85,7 @@ Taiga is made for developers and designers. We care enormously about UI because There are two possible ways to contribute to our UI: - **Bugs**: If you find a bug regarding front-end, please report it as previously indicated in the Bug reports section or send a pull-request as indicated in the Code Patches section. -- **Enhancements**: If its a design or UX bug or enhancement we will love to receive your feedback. Please send us your enhancement, with the reason and, if possible, an example. Our design and UX team will review your enhancement and fix it as soon as possible. We recommend you to use our [mailing list](http://groups.google.co.uk/d/forum/taigaio){target="_blank"} so we can have a lot of different opinions and debate. +- **Enhancements**: If its a design or UX bug or enhancement we will love to receive your feedback. Please send us your enhancement, with the reason and, if possible, an example. Our design and UX team will review your enhancement and fix it as soon as possible. We recommend you to use our [mailing list](http://groups.google.co.uk/d/forum/taigaio) so we can have a lot of different opinions and debate. - **Language Localization**: We are eager to offer localized versions of Taiga. Some members of the community have already volunteered to work to provide a variety of languages. We are working to implement some changes to allow for this and expect to accept these requests in the near future. diff --git a/app/coffee/app.coffee b/app/coffee/app.coffee index 5c4521e3..54ff9b5d 100644 --- a/app/coffee/app.coffee +++ b/app/coffee/app.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -63,9 +66,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven $routeProvider.when("/", { templateUrl: "home/home.html", - access: { - requiresLogin: true - }, + controller: "Home", + controllerAs: "vm" loader: true, title: "HOME.PAGE_TITLE", loader: true, @@ -74,6 +76,27 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven } ) + $routeProvider.when("/discover", + { + templateUrl: "discover/discover-home/discover-home.html", + controller: "DiscoverHome", + controllerAs: "vm", + title: "PROJECT.NAVIGATION.DISCOVER", + loader: true + } + ) + + $routeProvider.when("/discover/search", + { + templateUrl: "discover/discover-search/discover-search.html", + title: "PROJECT.NAVIGATION.DISCOVER", + loader: true, + controller: "DiscoverSearch", + controllerAs: "vm", + reloadOnSearch: false + } + ) + $routeProvider.when("/projects/", { templateUrl: "projects/listing/projects-listing.html", @@ -574,7 +597,7 @@ init = ($log, $rootscope, $auth, $events, $analytics, $translate, $location, $na $rootscope.$evalAsync(cb) $events.setupConnection() - + # Load user if $auth.isAuthenticated() user = $auth.getUser() @@ -661,6 +684,7 @@ modules = [ "taigaHome", "taigaUserTimeline", "taigaExternalApps", + "taigaDiscover", # template cache "templates", diff --git a/app/coffee/classes.coffee b/app/coffee/classes.coffee index a0d26f67..a3a74a31 100644 --- a/app/coffee/classes.coffee +++ b/app/coffee/classes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin.coffee b/app/coffee/modules/admin.coffee index 3a2322b5..f3a09d06 100644 --- a/app/coffee/modules/admin.coffee +++ b/app/coffee/modules/admin.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/lightboxes.coffee b/app/coffee/modules/admin/lightboxes.coffee index f130eb40..c625f007 100644 --- a/app/coffee/modules/admin/lightboxes.coffee +++ b/app/coffee/modules/admin/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee index 03025f81..84ff8071 100644 --- a/app/coffee/modules/admin/memberships.coffee +++ b/app/coffee/modules/admin/memberships.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/nav.coffee b/app/coffee/modules/admin/nav.coffee index 75fd57e0..e3fc1d71 100644 --- a/app/coffee/modules/admin/nav.coffee +++ b/app/coffee/modules/admin/nav.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index c12da801..4a6bee65 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -446,3 +449,64 @@ CsvIssueDirective = ($translate) -> } module.directive("tgCsvIssue", ["$translate", CsvIssueDirective]) + + +############################################################################# +## Project Logo Directive +############################################################################# + +ProjectLogoDirective = ($auth, $model, $rs, $confirm) -> + link = ($scope, $el, $attrs) -> + showSizeInfo = -> + $el.find(".size-info").addClass("active") + + onSuccess = (response) -> + project = $model.make_model("projects", response.data) + $scope.project = project + + $el.find('.loading-overlay').removeClass('active') + $confirm.notify('success') + + onError = (response) -> + showSizeInfo() if response.status == 413 + $el.find('.loading-overlay').removeClass('active') + $confirm.notify('error', response.data._error_message) + + # Change photo + $el.on "click", ".js-change-logo", -> + $el.find("#logo-field").click() + + $el.on "change", "#logo-field", (event) -> + if $scope.logoAttachment + $el.find('.loading-overlay').addClass("active") + $rs.projects.changeLogo($scope.project.id, $scope.logoAttachment).then(onSuccess, onError) + + # Use default photo + $el.on "click", "a.js-use-default-logo", (event) -> + $el.find('.loading-overlay').addClass("active") + $rs.projects.removeLogo($scope.project.id).then(onSuccess, onError) + + $scope.$on "$destroy", -> + $el.off() + + return {link:link} + +module.directive("tgProjectLogo", ["$tgAuth", "$tgModel", "$tgResources", "$tgConfirm", ProjectLogoDirective]) + + +############################################################################# +## Project Logo Model Directive +############################################################################# + +ProjectLogoModelDirective = ($parse) -> + link = ($scope, $el, $attrs) -> + model = $parse($attrs.tgProjectLogoModel) + modelSetter = model.assign + + $el.bind 'change', -> + $scope.$apply -> + modelSetter($scope, $el[0].files[0]) + + return {link:link} + +module.directive('tgProjectLogoModel', ['$parse', ProjectLogoModelDirective]) diff --git a/app/coffee/modules/admin/project-values.coffee b/app/coffee/modules/admin/project-values.coffee index f0f947f0..2fb76093 100644 --- a/app/coffee/modules/admin/project-values.coffee +++ b/app/coffee/modules/admin/project-values.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/roles.coffee b/app/coffee/modules/admin/roles.coffee index 3f67247b..59072640 100644 --- a/app/coffee/modules/admin/roles.coffee +++ b/app/coffee/modules/admin/roles.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/admin/third-parties.coffee b/app/coffee/modules/admin/third-parties.coffee index a5de9dc6..484de9b7 100644 --- a/app/coffee/modules/admin/third-parties.coffee +++ b/app/coffee/modules/admin/third-parties.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/auth.coffee b/app/coffee/modules/auth.coffee index 6a6ccc04..9a040fff 100644 --- a/app/coffee/modules/auth.coffee +++ b/app/coffee/modules/auth.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -200,14 +203,21 @@ module.service("$tgAuth", AuthService) # Directive that manages the visualization of public register # message/link on login page. -PublicRegisterMessageDirective = ($config, $navUrls, templates) -> +PublicRegisterMessageDirective = ($config, $navUrls, $routeParams, templates) -> template = templates.get("auth/login-text.html", true) templateFn = -> publicRegisterEnabled = $config.get("publicRegisterEnabled") if not publicRegisterEnabled return "" - return template({url:$navUrls.resolve("register")}) + + url = $navUrls.resolve("register") + if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("register") + nextUrl = encodeURIComponent($routeParams['next']) + console.log "-----", nextUrl + url += "?next=#{nextUrl}" + + return template({url:url}) return { restrict: "AE" @@ -215,22 +225,22 @@ PublicRegisterMessageDirective = ($config, $navUrls, templates) -> template: templateFn } -module.directive("tgPublicRegisterMessage", ["$tgConfig", "$tgNavUrls", "$tgTemplate", - PublicRegisterMessageDirective]) +module.directive("tgPublicRegisterMessage", ["$tgConfig", "$tgNavUrls", "$routeParams", + "$tgTemplate", PublicRegisterMessageDirective]) LoginDirective = ($auth, $confirm, $location, $config, $routeParams, $navUrls, $events, $translate) -> link = ($scope, $el, $attrs) -> form = new checksley.Form($el.find("form.login-form")) - onSuccess = (response) -> - if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("login") - nextUrl = decodeURIComponent($routeParams['next']) - else - nextUrl = $navUrls.resolve("home") + if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("login") + $scope.nextUrl = decodeURIComponent($routeParams['next']) + else + $scope.nextUrl = $navUrls.resolve("home") + onSuccess = (response) -> $events.setupConnection() - $location.url(nextUrl) + $location.url($scope.nextUrl) onError = (response) -> $confirm.notify("light-error", $translate.instant("LOGIN_FORM.ERROR_AUTH_INCORRECT")) @@ -268,7 +278,7 @@ module.directive("tgLogin", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgConfig" ## Register Directive ############################################################################# -RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics, $translate) -> +RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $routeParams, $analytics, $translate) -> link = ($scope, $el, $attrs) -> if not $config.get("publicRegisterEnabled") $location.path($navUrls.resolve("not-found")) @@ -277,12 +287,17 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics, $scope.data = {} form = $el.find("form").checksley({onlyOneErrorElement: true}) + if $routeParams['next'] and $routeParams['next'] != $navUrls.resolve("register") + $scope.nextUrl = decodeURIComponent($routeParams['next']) + else + $scope.nextUrl = $navUrls.resolve("home") + onSuccessSubmit = (response) -> $analytics.trackEvent("auth", "register", "user registration", 1) $confirm.notify("success", $translate.instant("LOGIN_FORM.SUCCESS")) - $location.path($navUrls.resolve("home")) + $location.url($scope.nextUrl) onErrorSubmit = (response) -> if response.data._error_message @@ -310,7 +325,7 @@ RegisterDirective = ($auth, $confirm, $location, $navUrls, $config, $analytics, return {link:link} module.directive("tgRegister", ["$tgAuth", "$tgConfirm", "$tgLocation", "$tgNavUrls", "$tgConfig", - "$tgAnalytics", "$translate", RegisterDirective]) + "$routeParams", "$tgAnalytics", "$translate", RegisterDirective]) ############################################################################# @@ -437,9 +452,7 @@ InvitationDirective = ($auth, $confirm, $location, $params, $navUrls, $analytics $confirm.notify("success", text) onErrorSubmitLogin = (response) -> - text = $translate.instant("INVITATION_LOGIN_FORM.ERROR") - - $confirm.notify("light-error", text) + $confirm.notify("light-error", response.data._error_message) submitLogin = debounce 2000, (event) => event.preventDefault() diff --git a/app/coffee/modules/backlog.coffee b/app/coffee/modules/backlog.coffee index 33eff5a6..173df9a7 100644 --- a/app/coffee/modules/backlog.coffee +++ b/app/coffee/modules/backlog.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/backlog/filters.coffee b/app/coffee/modules/backlog/filters.coffee index 21af3bef..b114ac14 100644 --- a/app/coffee/modules/backlog/filters.coffee +++ b/app/coffee/modules/backlog/filters.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/backlog/lightboxes.coffee b/app/coffee/modules/backlog/lightboxes.coffee index ca4e0156..72cd04e5 100644 --- a/app/coffee/modules/backlog/lightboxes.coffee +++ b/app/coffee/modules/backlog/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee index 2397b7c3..62f6fc2a 100644 --- a/app/coffee/modules/backlog/main.coffee +++ b/app/coffee/modules/backlog/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -51,11 +54,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F "$tgEvents", "$tgAnalytics", "$translate", - "$tgLoading" + "$tgLoading", + "tgResources" ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, - @location, @appMetaService, @navUrls, @events, @analytics, @translate, @loading) -> + @location, @appMetaService, @navUrls, @events, @analytics, @translate, @loading, @rs2) -> bindMethods(@) @scope.sectionName = @translate.instant("BACKLOG.SECTION_NAME") @@ -198,6 +202,9 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F @scope.sprintsCounter = sprints.length @scope.sprintsById = groupBy(sprints, (x) -> x.id) @rootscope.$broadcast("sprints:loaded", sprints) + + @scope.currentSprint = @.findCurrentSprint() + return sprints restoreFilters: -> @@ -560,10 +567,10 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F .timeout(200) .start() - @rs.userstories.getByRef(projectId, ref).then (us) => - @rootscope.$broadcast("usform:edit", us) - - currentLoading.finish() + return @rs.userstories.getByRef(projectId, ref).then (us) => + @rs2.attachments.list("us", us.id, projectId).then (attachments) => + @rootscope.$broadcast("usform:edit", us, attachments.toJS()) + currentLoading.finish() deleteUserStory: (us) -> title = @translate.instant("US.TITLE_DELETE_ACTION") @@ -591,6 +598,15 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F addNewSprint: () -> @rootscope.$broadcast("sprintform:create", @scope.projectId) + findCurrentSprint: () -> + currentDate = new Date().getTime() + + return _.find @scope.sprints, (sprint) -> + start = moment(sprint.estimated_start, 'YYYY-MM-DD').format('x') + end = moment(sprint.estimated_finish, 'YYYY-MM-DD').format('x') + + return currentDate >= start && currentDate <= end + module.controller("BacklogController", BacklogController) ############################################################################# @@ -641,7 +657,17 @@ BacklogDirective = ($repo, $rootscope, $translate) -> ## Move to current sprint link linkToolbar = ($scope, $el, $attrs, $ctrl) -> - moveToCurrentSprint = (selectedUss) -> + getUsToMove = () -> + # Calculating the us's to be modified + ussDom = $el.find(".backlog-table-body input:checkbox:checked") + + return _.map ussDom, (item) -> + item = $(item).closest('.tg-scope') + itemScope = item.scope() + itemScope.us.milestone = $scope.sprints[0].id + return itemScope.us + + moveUssToSprint = (selectedUss, sprint) -> ussCurrent = _($scope.userstories) # Remove them from backlog @@ -651,30 +677,36 @@ BacklogDirective = ($repo, $rootscope, $translate) -> totalExtraPoints = _.reduce(extraPoints, (acc, num) -> acc + num) # Add them to current sprint - $scope.sprints[0].user_stories = _.union($scope.sprints[0].user_stories, selectedUss) + sprint.user_stories = _.union(sprint.user_stories, selectedUss) # Update the total of points - $scope.sprints[0].total_points += totalExtraPoints + sprint.total_points += totalExtraPoints $repo.saveAll(selectedUss).then -> $ctrl.loadSprints() $ctrl.loadProjectStats() + $el.find(".move-to-sprint").hide() + + moveToCurrentSprint = (selectedUss) -> + moveUssToSprint(selectedUss, $scope.currentSprint) + + moveToLatestSprint = (selectedUss) -> + moveUssToSprint(selectedUss, $scope.sprints[0]) shiftPressed = false lastChecked = null checkSelected = (target) -> lastChecked = target.closest(".us-item-row") - moveToCurrentSprintDom = $el.find("#move-to-current-sprint") + target.closest('.us-item-row').toggleClass('ui-multisortable-multiple') + moveToSprintDom = $el.find(".move-to-sprint") selectedUsDom = $el.find(".backlog-table-body input:checkbox:checked") if selectedUsDom.length > 0 and $scope.sprints.length > 0 - moveToCurrentSprintDom.show() + moveToSprintDom.show() else - moveToCurrentSprintDom.hide() - - target.closest('.us-item-row').toggleClass('ui-multisortable-multiple') + moveToSprintDom.hide() $(window).on "keydown.shift-pressed keyup.shift-pressed", (event) -> shiftPressed = !!event.shiftKey @@ -704,15 +736,13 @@ BacklogDirective = ($repo, $rootscope, $translate) -> target.closest(".us-item-row").toggleClass('is-checked') checkSelected(target) - $el.on "click", "#move-to-current-sprint", (event) => - # Calculating the us's to be modified - ussDom = $el.find(".backlog-table-body input:checkbox:checked") + $el.on "click", "#move-to-latest-sprint", (event) => + ussToMove = getUsToMove() - ussToMove = _.map ussDom, (item) -> - item = $(item).closest('.tg-scope') - itemScope = item.scope() - itemScope.us.milestone = $scope.sprints[0].id - return itemScope.us + $scope.$apply(_.partial(moveToLatestSprint, ussToMove)) + + $el.on "click", "#move-to-current-sprint", (event) => + ussToMove = getUsToMove() $scope.$apply(_.partial(moveToCurrentSprint, ussToMove)) diff --git a/app/coffee/modules/backlog/sortable.coffee b/app/coffee/modules/backlog/sortable.coffee index a7148f8f..e674049f 100644 --- a/app/coffee/modules/backlog/sortable.coffee +++ b/app/coffee/modules/backlog/sortable.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/backlog/sprints.coffee b/app/coffee/modules/backlog/sprints.coffee index 19211c25..218da013 100644 --- a/app/coffee/modules/backlog/sprints.coffee +++ b/app/coffee/modules/backlog/sprints.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base.coffee b/app/coffee/modules/base.coffee index c07d6714..feeb02f8 100644 --- a/app/coffee/modules/base.coffee +++ b/app/coffee/modules/base.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -49,6 +52,9 @@ urls = { "not-found": "/not-found" "permission-denied": "/permission-denied" + "discover": "/discover" + "discover-search": "/discover/search" + "login": "/login" "forgot-password": "/forgot-password" "change-password": "/change-password/:token" diff --git a/app/coffee/modules/base/bind.coffee b/app/coffee/modules/base/bind.coffee index 7b8c6aac..91e6735c 100644 --- a/app/coffee/modules/base/bind.coffee +++ b/app/coffee/modules/base/bind.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/conf.coffee b/app/coffee/modules/base/conf.coffee index e72a3351..20f56387 100644 --- a/app/coffee/modules/base/conf.coffee +++ b/app/coffee/modules/base/conf.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/contrib.coffee b/app/coffee/modules/base/contrib.coffee index 71293d2f..9c66492f 100644 --- a/app/coffee/modules/base/contrib.coffee +++ b/app/coffee/modules/base/contrib.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/filters.coffee b/app/coffee/modules/base/filters.coffee index 0cffc3b7..9b9acc2c 100644 --- a/app/coffee/modules/base/filters.coffee +++ b/app/coffee/modules/base/filters.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/http.coffee b/app/coffee/modules/base/http.coffee index 0ccf80ac..2144e3fb 100644 --- a/app/coffee/modules/base/http.coffee +++ b/app/coffee/modules/base/http.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/location.coffee b/app/coffee/modules/base/location.coffee index 21d844ca..8bfd9905 100644 --- a/app/coffee/modules/base/location.coffee +++ b/app/coffee/modules/base/location.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/model.coffee b/app/coffee/modules/base/model.coffee index 01743de0..e91a3a0b 100644 --- a/app/coffee/modules/base/model.coffee +++ b/app/coffee/modules/base/model.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/navurls.coffee b/app/coffee/modules/base/navurls.coffee index fd357df8..85fee430 100644 --- a/app/coffee/modules/base/navurls.coffee +++ b/app/coffee/modules/base/navurls.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -114,7 +117,7 @@ NavigationUrlsDirective = ($navurls, $auth, $q, $location) -> $el.on "mouseenter", (event) -> target = $(event.currentTarget) - if !target.data("fullUrl") + if !target.data("fullUrl") || $attrs.tgNavGetParams != target.data("params") parseNav($attrs.tgNav, $scope).then (result) -> [name, options] = result user = $auth.getUser() @@ -128,6 +131,8 @@ NavigationUrlsDirective = ($navurls, $auth, $q, $location) -> getURLParamsStr = $.param(getURLParams) fullUrl = "#{fullUrl}?#{getURLParamsStr}" + target.data("params", $attrs.tgNavGetParams) + target.data("fullUrl", fullUrl) if target.is("a") diff --git a/app/coffee/modules/base/repository.coffee b/app/coffee/modules/base/repository.coffee index 0156dd15..268a86a6 100644 --- a/app/coffee/modules/base/repository.coffee +++ b/app/coffee/modules/base/repository.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/storage.coffee b/app/coffee/modules/base/storage.coffee index e3e5fc2e..c8215712 100644 --- a/app/coffee/modules/base/storage.coffee +++ b/app/coffee/modules/base/storage.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/base/urls.coffee b/app/coffee/modules/base/urls.coffee index 33977970..44fdfaf6 100644 --- a/app/coffee/modules/base/urls.coffee +++ b/app/coffee/modules/base/urls.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common.coffee b/app/coffee/modules/common.coffee index 03fc5687..955861d0 100644 --- a/app/coffee/modules/common.coffee +++ b/app/coffee/modules/common.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -93,22 +96,38 @@ module.factory("$selectedText", ["$window", "$document", SelectedText]) ## Permission directive, hide elements when necessary ############################################################################# -CheckPermissionDirective = -> +CheckPermissionDirective = (projectService) -> render = ($el, project, permission) -> - $el.removeClass('hidden') if project.my_permissions.indexOf(permission) > -1 + if project && permission + $el.removeClass('hidden') if project.get('my_permissions').indexOf(permission) > -1 link = ($scope, $el, $attrs) -> $el.addClass('hidden') permission = $attrs.tgCheckPermission - $scope.$watch "project", (project) -> - render($el, project, permission) if project? + unwatch = $scope.$watch () -> + return projectService.project + , () -> + return if !projectService.project + + render($el, projectService.project, permission) + unwatch() + + unObserve = $attrs.$observe "tgCheckPermission", (permission) -> + return if !permission + + render($el, projectService.project, permission) + unObserve() $scope.$on "$destroy", -> $el.off() return {link:link} +CheckPermissionDirective.$inject = [ + "tgProjectService" +] + module.directive("tgCheckPermission", CheckPermissionDirective) ############################################################################# diff --git a/app/coffee/modules/common/analytics.coffee b/app/coffee/modules/common/analytics.coffee index 237e2619..02950003 100644 --- a/app/coffee/modules/common/analytics.coffee +++ b/app/coffee/modules/common/analytics.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/attachments.coffee b/app/coffee/modules/common/attachments.coffee deleted file mode 100644 index c6ba068f..00000000 --- a/app/coffee/modules/common/attachments.coffee +++ /dev/null @@ -1,340 +0,0 @@ -### -# Copyright (C) 2014-2016 Andrey Antukh -# Copyright (C) 2014-2016 Jesús Espino Garcia -# Copyright (C) 2014-2016 David Barragán Merino -# -# 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: modules/common/attachments.coffee -### - -taiga = @.taiga -sizeFormat = @.taiga.sizeFormat -bindOnce = @.taiga.bindOnce -bindMethods = @.taiga.bindMethods - -module = angular.module("taigaCommon") - - -class AttachmentsController extends taiga.Controller - @.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q", "$translate"] - - constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q, @translate) -> - bindMethods(@) - @.type = null - @.objectId = null - @.projectId = null - - @.uploadingAttachments = [] - @.attachments = [] - @.attachmentsCount = 0 - @.deprecatedAttachmentsCount = 0 - @.showDeprecated = false - - initialize: (type, objectId) -> - @.type = type - @.objectId = objectId - @.projectId = @scope.projectId - - loadAttachments: -> - return @.attachments if not @.objectId - - urlname = "attachments/#{@.type}" - - return @rs.attachments.list(urlname, @.objectId, @.projectId).then (attachments) => - @.attachments = _.sortBy(attachments, "order") - @.updateCounters() - return attachments - - updateCounters: -> - @.attachmentsCount = @.attachments.length - @.deprecatedAttachmentsCount = _.filter(@.attachments, {is_deprecated: true}).length - - _createAttachment: (attachment) -> - urlName = "attachments/#{@.type}" - - promise = @rs.attachments.create(urlName, @.projectId, @.objectId, attachment) - promise = promise.then (data) => - data.isCreatedRightNow = true - - index = @.uploadingAttachments.indexOf(attachment) - @.uploadingAttachments.splice(index, 1) - @.attachments.push(data) - @rootscope.$broadcast("attachment:create") - - promise = promise.then null, (data) => - @scope.$emit("attachments:size-error") if data.status == 413 - - index = @.uploadingAttachments.indexOf(attachment) - @.uploadingAttachments.splice(index, 1) - - message = @translate.instant("ATTACHMENT.ERROR_UPLOAD_ATTACHMENT", { - fileName: attachment.name, errorMessage: data.data._error_message}) - @confirm.notify("error", message) - return @q.reject(data) - - return promise - - # Create attachments in bulk - createAttachments: (attachments) -> - promises = _.map(attachments, (x) => @._createAttachment(x)) - return @q.all(promises).then => - @.updateCounters() - - # Add uploading attachment tracking. - addUploadingAttachments: (attachments) -> - @.uploadingAttachments = _.union(@.uploadingAttachments, attachments) - - # Change order of attachment in a ordered list. - # This function is mainly executed after sortable ends. - reorderAttachment: (attachment, newIndex) -> - oldIndex = @.attachments.indexOf(attachment) - return if oldIndex == newIndex - - @.attachments.splice(oldIndex, 1) - @.attachments.splice(newIndex, 0, attachment) - - _.each(@.attachments, (x,i) -> x.order = i+1) - - # Persist one concrete attachment. - # This function is mainly used when user clicks - # to save button for save one unique attachment. - updateAttachment: (attachment) -> - onSuccess = => - @.updateCounters() - @rootscope.$broadcast("attachment:edit") - - onError = (response) => - $scope.$emit("attachments:size-error") if response.status == 413 - @confirm.notify("error") - return @q.reject() - - return @repo.save(attachment).then(onSuccess, onError) - - # Persist all pending modifications on attachments. - # This function is used mainly for persist the order - # after sorting. - saveAttachments: -> - return @repo.saveAll(@.attachments).then null, => - for item in @.attachments - item.revert() - @.attachments = _.sortBy(@.attachments, "order") - - # Remove one concrete attachment. - removeAttachment: (attachment) -> - title = @translate.instant("ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT") - message = @translate.instant("ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT", {fileName: attachment.name}) - - return @confirm.askOnDelete(title, message).then (askResponse) => - onSuccess = => - askResponse.finish() - index = @.attachments.indexOf(attachment) - @.attachments.splice(index, 1) - @.updateCounters() - @rootscope.$broadcast("attachment:delete") - - onError = => - askResponse.finish(false) - message = @translate.instant("ATTACHMENT.ERROR_DELETE_ATTACHMENT", {errorMessage: message}) - @confirm.notify("error", null, message) - return @q.reject() - - return @repo.remove(attachment).then(onSuccess, onError) - - # Function used in template for filter visible attachments - filterAttachments: (item) -> - if @.showDeprecated - return true - return not item.is_deprecated - - -AttachmentsDirective = ($config, $confirm, $templates, $translate) -> - template = $templates.get("attachment/attachments.html", true) - - link = ($scope, $el, $attrs, $ctrls) -> - $ctrl = $ctrls[0] - $model = $ctrls[1] - - bindOnce $scope, $attrs.ngModel, (value) -> - $ctrl.initialize($attrs.type, value.id) - $ctrl.loadAttachments() - - tdom = $el.find("div.attachment-body.sortable") - tdom.sortable({ - items: "div.single-attachment" - handle: "a.settings.icon.icon-drag-v" - containment: ".attachments" - dropOnEmpty: true - scroll: false - tolerance: "pointer" - placeholder: "sortable-placeholder single-attachment" - }) - - tdom.on "sortstop", (event, ui) -> - attachment = ui.item.scope().attach - newIndex = ui.item.index() - - $ctrl.reorderAttachment(attachment, newIndex) - $ctrl.saveAttachments().then -> - $scope.$emit("attachment:edit") - - showSizeInfo = -> - $el.find(".size-info").removeClass("hidden") - - $scope.$on "attachments:size-error", -> - showSizeInfo() - - $el.on "change", ".attachments-header input", (event) -> - files = _.toArray(event.target.files) - - return if files.length < 1 - - $scope.$apply -> - $ctrl.addUploadingAttachments(files) - $ctrl.createAttachments(files) - - $el.on "click", ".more-attachments", (event) -> - event.preventDefault() - target = angular.element(event.currentTarget) - - $scope.$apply -> - $ctrl.showDeprecated = not $ctrl.showDeprecated - - target.find("span.text").addClass("hidden") - if $ctrl.showDeprecated - target.find("span[data-type=hide]").removeClass("hidden") - target.find("more-attachments-num").addClass("hidden") - else - target.find("span[data-type=show]").removeClass("hidden") - target.find("more-attachments-num").removeClass("hidden") - - $scope.$on "$destroy", -> - $el.off() - - templateFn = ($el, $attrs) -> - maxFileSize = $config.get("maxUploadFileSize", null) - maxFileSize = sizeFormat(maxFileSize) if maxFileSize - maxFileSizeMsg = if maxFileSize then $translate.instant("ATTACHMENT.MAX_UPLOAD_SIZE", {maxFileSize: maxFileSize}) else "" - ctx = { - type: $attrs.type - maxFileSize: maxFileSize - maxFileSizeMsg: maxFileSizeMsg - } - return template(ctx) - - return { - require: ["tgAttachments", "ngModel"] - controller: AttachmentsController - controllerAs: "ctrl" - restrict: "AE" - scope: true - link: link - template: templateFn - } - -module.directive("tgAttachments", ["$tgConfig", "$tgConfirm", "$tgTemplate", "$translate", AttachmentsDirective]) - - -AttachmentDirective = ($template, $compile, $translate, $rootScope) -> - template = $template.get("attachment/attachment.html", true) - templateEdit = $template.get("attachment/attachment-edit.html", true) - - link = ($scope, $el, $attrs, $ctrl) -> - render = (attachment, edit=false) -> - permissions = $scope.project.my_permissions - modifyPermission = permissions.indexOf("modify_#{$ctrl.type}") > -1 - - ctx = { - id: attachment.id - name: attachment.name - title : $translate.instant("ATTACHMENT.TITLE", { - fileName: attachment.name, - date: moment(attachment.created_date).format($translate.instant("ATTACHMENT.DATE"))}) - url: attachment.url - size: sizeFormat(attachment.size) - description: attachment.description - isDeprecated: attachment.is_deprecated - modifyPermission: modifyPermission - } - - if edit - html = $compile(templateEdit(ctx))($scope) - else - html = $compile(template(ctx))($scope) - - $el.html(html) - - if attachment.is_deprecated - $el.addClass("deprecated") - $el.find("input:checkbox").prop('checked', true) - else - $el.removeClass("deprecated") - - saveAttachment = -> - attachment.description = $el.find("input[name='description']").val() - attachment.is_deprecated = $el.find("input[name='is-deprecated']").prop("checked") - attachment.isCreatedRightNow = false - - $scope.$apply -> - $ctrl.updateAttachment(attachment).then -> - render(attachment, false) - - ## Actions (on edit mode) - $el.on "click", "a.editable-settings.icon-floppy", (event) -> - event.preventDefault() - saveAttachment() - - $el.on "keyup", "input[name=description]", (event) -> - if event.keyCode == 13 - saveAttachment() - else if event.keyCode == 27 - $scope.$apply -> render(attachment, false) - - $el.on "click", "a.editable-settings.icon-delete", (event) -> - event.preventDefault() - render(attachment, false) - - ## Actions (on view mode) - $el.on "click", "a.settings.icon-edit", (event) -> - event.preventDefault() - render(attachment, true) - $el.find("input[name='description']").focus().select() - - $el.on "click", "a.settings.icon-delete", (event) -> - event.preventDefault() - $scope.$apply -> - $ctrl.removeAttachment(attachment) - - $el.on "click", "div.attachment-name a", (event) -> - if null != attachment.name.match(/\.(jpe?g|png|gif|gifv|webm)/i) - event.preventDefault() - $scope.$apply -> - $rootScope.$broadcast("attachment:preview", attachment) - - $scope.$on "$destroy", -> - $el.off() - - # Bootstrap - attachment = $scope.$eval($attrs.tgAttachment) - render(attachment, attachment.isCreatedRightNow) - if attachment.isCreatedRightNow - $el.find("input[name='description']").focus().select() - - return { - link: link - require: "^tgAttachments" - restrict: "AE" - } - -module.directive("tgAttachment", ["$tgTemplate", "$compile", "$translate", "$rootScope", AttachmentDirective]) diff --git a/app/coffee/modules/common/components.coffee b/app/coffee/modules/common/components.coffee index 0a32e803..32b61689 100644 --- a/app/coffee/modules/common/components.coffee +++ b/app/coffee/modules/common/components.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -182,13 +185,13 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile, promise = $repo.save($model.$modelValue) promise.then -> - $confirm.notify("success") watchers = _.map(watchers, (watcherId) -> $scope.usersById[watcherId]) renderWatchers(watchers) $rootscope.$broadcast("object:updated") promise.then null, -> $model.$modelValue.revert() + $confirm.notify("error") deleteWatcher = $qqueue.bindAdd (watcherIds) => item = $model.$modelValue.clone() @@ -197,7 +200,6 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile, promise = $repo.save($model.$modelValue) promise.then -> - $confirm.notify("success") watchers = _.map(item.watchers, (watcherId) -> $scope.usersById[watcherId]) renderWatchers(watchers) $rootscope.$broadcast("object:updated") @@ -205,7 +207,6 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile, item.revert() $confirm.notify("error") - renderWatchers = (watchers) -> ctx = { watchers: watchers @@ -232,12 +233,6 @@ WatchersDirective = ($rootscope, $confirm, $repo, $qqueue, $template, $compile, deleteWatcher(watcherIds) - $el.on "click", ".js-add-watcher", (event) -> - event.preventDefault() - return if not isEditable() - $scope.$apply -> - $rootscope.$broadcast("watcher:add", $model.$modelValue) - $scope.$on "watcher:added", (ctx, watcherId) -> watchers = _.clone($model.$modelValue.watchers, false) watchers.push(watcherId) @@ -263,7 +258,7 @@ module.directive("tgWatchers", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgQqueu ## Assigned to directive ############################################################################# -AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template, $translate, $compile) -> +AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template, $translate, $compile, $currentUserService) -> # You have to include a div with the tg-lb-assignedto directive in the page # where use this directive template = $template.get("common/components/assigned-to.html", true) @@ -282,7 +277,6 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template promise = $repo.save($model.$modelValue) promise.then -> currentLoading.finish() - $confirm.notify("success") renderAssignedTo($model.$modelValue) $rootscope.$broadcast("object:updated") promise.then null, -> @@ -292,13 +286,24 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template return promise - renderAssignedTo = (issue) -> - assignedToId = issue?.assigned_to - assignedTo = if assignedToId? then $scope.usersById[assignedToId] else null + renderAssignedTo = (assignedObject) -> + if assignedObject?.assigned_to? + fullName = assignedObject.assigned_to_extra_info.full_name_display + photo = assignedObject.assigned_to_extra_info.photo + isUnassigned = false + else + fullName = $translate.instant("COMMON.ASSIGNED_TO.ASSIGN") + photo = "/#{window._version}/images/unnamed.png" + isUnassigned = true + + isIocaine = assignedObject?.is_iocaine ctx = { - assignedTo: assignedTo + fullName: fullName + photo: photo + isUnassigned: isUnassigned isEditable: isEditable() + isIocaine: isIocaine } html = $compile(template(ctx))($scope) $el.html(html) @@ -309,6 +314,12 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template $scope.$apply -> $rootscope.$broadcast("assigned-to:add", $model.$modelValue) + $el.on "click", ".assign-to-me", (event) -> + event.preventDefault() + return if not isEditable() + $model.$modelValue.assigned_to = $currentUserService.getUser().get('id') + save($currentUserService.getUser().get('id')) + $el.on "click", ".icon-delete", (event) -> event.preventDefault() return if not isEditable() @@ -335,7 +346,7 @@ AssignedToDirective = ($rootscope, $confirm, $repo, $loading, $qqueue, $template require:"ngModel" } -module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQqueue", "$tgTemplate", "$translate", "$compile", +module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading", "$tgQqueue", "$tgTemplate", "$translate", "$compile","tgCurrentUserService", AssignedToDirective]) @@ -357,11 +368,11 @@ BlockButtonDirective = ($rootscope, $loading, $template) -> $el.find('.item-block').addClass('editable') if item.is_blocked - $el.find('.item-block').hide() - $el.find('.item-unblock').show() + $el.find('.item-block').removeClass('is-active') + $el.find('.item-unblock').addClass('is-active') else - $el.find('.item-block').show() - $el.find('.item-unblock').hide() + $el.find('.item-block').addClass('is-active') + $el.find('.item-unblock').removeClass('is-active') $el.on "click", ".item-block", (event) -> event.preventDefault() @@ -604,8 +615,78 @@ EditableDescriptionDirective = ($rootscope, $repo, $confirm, $compile, $loading, template: template } -module.directive("tgEditableDescription", ["$rootScope", "$tgRepo", "$tgConfirm", "$compile", "$tgLoading", - "$selectedText", "$tgQqueue", "$tgTemplate", EditableDescriptionDirective]) +module.directive("tgEditableDescription", [ + "$rootScope", + "$tgRepo", + "$tgConfirm", + "$compile", + "$tgLoading", + "$selectedText", + "$tgQqueue", + "$tgTemplate", EditableDescriptionDirective]) + + + +EditableWysiwyg = (attachmentsService, attachmentsFullService) -> + link = ($scope, $el, $attrs, $model) -> + + isInEditMode = -> + return $el.find('textarea').is(':visible') + + + uploadFile = (file, type) -> + return if !attachmentsService.validate(file) + + return attachmentsFullService.addAttachment($model.$modelValue.project, $model.$modelValue.id, type, file).then (result) -> + if taiga.isImage(result.getIn(['file', 'name'])) + return '![' + result.getIn(['file', 'name']) + '](' + result.getIn(['file', 'url']) + ')' + else + return '[' + result.getIn(['file', 'name']) + '](' + result.getIn(['file', 'url']) + ')' + + $el.on 'dragover', (e) -> + textarea = $el.find('textarea').focus() + + return false + + $el.on 'drop', (e) -> + e.stopPropagation() + e.preventDefault() + + if isInEditMode() + dataTransfer = e.dataTransfer || (e.originalEvent && e.originalEvent.dataTransfer) + + textarea = $el.find('textarea') + + textarea.addClass('in-progress') + + type = $model.$modelValue['_name'] + + if type == "userstories" + type = "us" + else if type == "tasks" + type = "task" + else if type == "issues" + type = "issue" + else if type == "wiki" + type = "wiki_page" + + promises = _.map dataTransfer.files, (file) -> + return uploadFile(file, type) + + Promise.all(promises).then (result) -> + textarea = $el.find('textarea') + + $.markItUp({ replaceWith: result.join(' ') }) + + textarea.removeClass('in-progress') + + return { + link: link + restrict: "EA" + require: "ngModel" + } + +module.directive("tgEditableWysiwyg", ["tgAttachmentsService", "tgAttachmentsFullService", EditableWysiwyg]) ############################################################################# @@ -637,13 +718,16 @@ ListItemTaskStatusDirective = -> module.directive("tgListitemTaskStatus", ListItemTaskStatusDirective) -ListItemAssignedtoDirective = ($template) -> +ListItemAssignedtoDirective = ($template, $translate) -> template = $template.get("common/components/list-item-assigned-to-avatar.html", true) link = ($scope, $el, $attrs) -> bindOnce $scope, "usersById", (usersById) -> item = $scope.$eval($attrs.tgListitemAssignedto) - ctx = {name: "Unassigned", imgurl: "/" + window._version + "/images/unnamed.png"} + ctx = { + name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"), + imgurl: "/#{window._version}/images/unnamed.png" + } member = usersById[item.assigned_to] if member @@ -654,7 +738,7 @@ ListItemAssignedtoDirective = ($template) -> return {link:link} -module.directive("tgListitemAssignedto", ["$tgTemplate", ListItemAssignedtoDirective]) +module.directive("tgListitemAssignedto", ["$tgTemplate", "$translate", ListItemAssignedtoDirective]) ListItemIssueStatusDirective = -> @@ -770,7 +854,7 @@ module.directive("tgProgressBar", ["$tgTemplate", TgProgressBarDirective]) TgMainTitleDirective = ($translate) -> link = ($scope, $el, $attrs) -> $attrs.$observe "i18nSectionName", (i18nSectionName) -> - $scope.sectionName = $translate.instant(i18nSectionName) + $scope.sectionName = i18nSectionName $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/common/confirm.coffee b/app/coffee/modules/common/confirm.coffee index e77329b1..be7452cc 100644 --- a/app/coffee/modules/common/confirm.coffee +++ b/app/coffee/modules/common/confirm.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/custom-field-values.coffee b/app/coffee/modules/common/custom-field-values.coffee index 5e3c6383..ad748350 100644 --- a/app/coffee/modules/common/custom-field-values.coffee +++ b/app/coffee/modules/common/custom-field-values.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/estimation.coffee b/app/coffee/modules/common/estimation.coffee index 1a58ce8d..6cf8417f 100644 --- a/app/coffee/modules/common/estimation.coffee +++ b/app/coffee/modules/common/estimation.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -28,7 +31,7 @@ module = angular.module("taigaCommon") ## User story estimation directive (for Lightboxes) ############################################################################# -LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $template, $compile) -> +LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $template, $compile) -> # Display the points of a US and you can edit it. # # Example: @@ -69,14 +72,15 @@ LbUsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $ require: "ngModel" } -module.directive("tgLbUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgTemplate", "$compile", LbUsEstimationDirective]) +module.directive("tgLbUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgTemplate", + "$compile", LbUsEstimationDirective]) ############################################################################# ## User story estimation directive ############################################################################# -UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qqueue, $template, $compile) -> +UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $qqueue, $template, $compile) -> # Display the points of a US and you can edit it. # # Example: @@ -117,8 +121,8 @@ UsEstimationDirective = ($tgEstimationsService, $rootScope, $repo, $confirm, $qq require: "ngModel" } -module.directive("tgUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgConfirm", "$tgQqueue", "$tgTemplate", "$compile" - UsEstimationDirective]) +module.directive("tgUsEstimation", ["$tgEstimationsService", "$rootScope", "$tgRepo", "$tgQqueue", + "$tgTemplate", "$compile", UsEstimationDirective]) ############################################################################# @@ -142,7 +146,6 @@ EstimationsService = ($template, $qqueue, $repo, $confirm, $q) -> $qqueue.add () => onSuccess = => deferred.resolve() - $confirm.notify("success") onError = => $confirm.notify("error") @@ -244,4 +247,5 @@ EstimationsService = ($template, $qqueue, $repo, $confirm, $q) -> create: create } -module.factory("$tgEstimationsService", ["$tgTemplate", "$tgQqueue", "$tgRepo", "$tgConfirm", "$q", EstimationsService]) +module.factory("$tgEstimationsService", ["$tgTemplate", "$tgQqueue", "$tgRepo", "$tgConfirm", + "$q", EstimationsService]) diff --git a/app/coffee/modules/common/filters.coffee b/app/coffee/modules/common/filters.coffee index 140d859e..7590b45c 100644 --- a/app/coffee/modules/common/filters.coffee +++ b/app/coffee/modules/common/filters.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -65,3 +68,9 @@ momentFromNow = -> return "" module.filter("momentFromNow", momentFromNow) + + +sizeFormat = => + return @.taiga.sizeFormat + +module.filter("sizeFormat", sizeFormat) diff --git a/app/coffee/modules/common/history.coffee b/app/coffee/modules/common/history.coffee index 12b623aa..ef90a356 100644 --- a/app/coffee/modules/common/history.coffee +++ b/app/coffee/modules/common/history.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -302,6 +305,7 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm, $translate, $c changesText: renderChangesHelperText(comment) changes: renderChangeEntries(comment) mode: "comment" + deleteCommentActionTitle: $translate.instant("COMMENTS.DELETE") deleteCommentDate: moment(comment.delete_comment_date).format(getPrettyDateFormat()) if comment.delete_comment_date deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name activityId: comment.id diff --git a/app/coffee/modules/common/importer.coffee b/app/coffee/modules/common/importer.coffee index 65803eb9..de61ce3f 100644 --- a/app/coffee/modules/common/importer.coffee +++ b/app/coffee/modules/common/importer.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index 13cb9d9a..57f688b7 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -24,6 +27,7 @@ module = angular.module("taigaCommon") bindOnce = @.taiga.bindOnce timeout = @.taiga.timeout debounce = @.taiga.debounce +sizeFormat = @.taiga.sizeFormat ############################################################################# ## Common Lightbox Services @@ -262,13 +266,30 @@ module.directive("tgBlockingMessageInput", ["$log", "$tgTemplate", "$compile", B ## Create/Edit Userstory Lightbox Directive ############################################################################# -CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading, $translate) -> +CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading, $translate, $confirm, $q, attachmentsService) -> link = ($scope, $el, attrs) -> + $scope.createEditUs = {} $scope.isNew = true + attachmentsToAdd = Immutable.List() + attachmentsToDelete = Immutable.List() + + resetAttachments = () -> + attachmentsToAdd = Immutable.List() + attachmentsToDelete = Immutable.List() + + $scope.addAttachment = (attachment) -> + attachmentsToAdd = attachmentsToAdd.push(attachment) + + $scope.deleteAttachment = (attachment) -> + attachmentsToDelete = attachmentsToDelete.push(attachment) + $scope.$on "usform:new", (ctx, projectId, status, statusList) -> $scope.isNew = true $scope.usStatusList = statusList + $scope.attachments = Immutable.List() + + resetAttachments() $scope.us = $model.make_model("userstories", { project: projectId @@ -290,10 +311,13 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, lightboxService.open($el) - $scope.$on "usform:edit", (ctx, us) -> + $scope.$on "usform:edit", (ctx, us, attachments) -> $scope.us = us + $scope.attachments = Immutable.fromJS(attachments) $scope.isNew = false + resetAttachments() + # Update texts for edition $el.find(".button-green").html($translate.instant("COMMON.SAVE")) $el.find(".title").html($translate.instant("LIGHTBOX.CREATE_EDIT_US.EDIT_US")) @@ -318,6 +342,18 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, lightboxService.open($el) + createAttachments = (obj) -> + promises = _.map attachmentsToAdd.toJS(), (attachment) -> + attachmentsService.upload(attachment.file, obj.id, $scope.us.project, 'us') + + return $q.all(promises) + + deleteAttachments = (obj) -> + promises = _.map attachmentsToDelete.toJS(), (attachment) -> + return attachmentsService.delete("us", attachment.id) + + return $q.all(promises) + submit = debounce 2000, (event) => event.preventDefault() @@ -336,6 +372,11 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, promise = $repo.save($scope.us) broadcastEvent = "usform:edit:success" + promise.then (data) -> + deleteAttachments(data).then () => createAttachments(data) + + return data + promise.then (data) -> currentLoading.finish() lightboxService.close($el) @@ -377,6 +418,9 @@ module.directive("tgLbCreateEditUserstory", [ "lightboxService", "$tgLoading", "$translate", + "$tgConfirm", + "$q", + "tgAttachmentsService", CreateEditUserstoryDirective ]) @@ -629,30 +673,14 @@ module.directive("tgLbWatchers", ["$tgRepo", "lightboxService", "lightboxKeyboar ## Attachment Preview Lighbox ############################################################################# -AttachmentPreviewLightboxDirective = ($repo, lightboxService, lightboxKeyboardNavigationService, $template, $compile) -> +AttachmentPreviewLightboxDirective = (lightboxService, $template, $compile) -> link = ($scope, $el, attrs) -> - template = $template.get("common/lightbox/lightbox-attachment-preview.html", true) - - $scope.$on "attachment:preview", (event, attachment) -> - lightboxService.open($el) - render(attachment) - - $scope.$on "$destroy", -> - $el.off() - - render = (attachment) -> - ctx = { - url: attachment.url, - title: attachment.description, - name: attachment.name - } - - html = template(ctx) - html = $compile(html)($scope) - $el.html(html) + lightboxService.open($el) return { - link: link + templateUrl: 'common/lightbox/lightbox-attachment-preview.html', + link: link, + scope: true } -module.directive("tgLbAttachmentPreview", ["$tgRepo", "lightboxService", "lightboxKeyboardNavigationService", "$tgTemplate", "$compile", AttachmentPreviewLightboxDirective]) +module.directive("tgLbAttachmentPreview", ["lightboxService", "$tgTemplate", "$compile", AttachmentPreviewLightboxDirective]) diff --git a/app/coffee/modules/common/loader.coffee b/app/coffee/modules/common/loader.coffee index bcd2a3e4..80253c91 100644 --- a/app/coffee/modules/common/loader.coffee +++ b/app/coffee/modules/common/loader.coffee @@ -1,9 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino # Copyright (C) 2014-2016 Juan Francisco Alcántara # Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/loading.coffee b/app/coffee/modules/common/loading.coffee index e8a5af2f..2c128ba9 100644 --- a/app/coffee/modules/common/loading.coffee +++ b/app/coffee/modules/common/loading.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -109,7 +112,7 @@ LoadingDirective = ($loading) -> if showLoading currentLoading = $loading() .target($el) - .timeout(50) + .timeout(100) .template(template) .scope($scope) .start() diff --git a/app/coffee/modules/common/popovers.coffee b/app/coffee/modules/common/popovers.coffee index 91db16d4..fe1504ad 100644 --- a/app/coffee/modules/common/popovers.coffee +++ b/app/coffee/modules/common/popovers.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/raven-logger.coffee b/app/coffee/modules/common/raven-logger.coffee index 9b316e86..c6ca9077 100644 --- a/app/coffee/modules/common/raven-logger.coffee +++ b/app/coffee/modules/common/raven-logger.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/common/tags.coffee b/app/coffee/modules/common/tags.coffee index be5852f9..0b8c8dfb 100644 --- a/app/coffee/modules/common/tags.coffee +++ b/app/coffee/modules/common/tags.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -283,10 +286,11 @@ TagLineDirective = ($rootScope, $repo, $rs, $confirm, $qqueue, $template, $compi $confirm.notify("error") model.revert() $model.$setViewValue(model) - $repo.save(model).then(onSuccess, onError) hideSaveButton() + return $repo.save(model).then(onSuccess, onError) + deleteValue = $qqueue.bindAdd (value) -> value = trim(value.toLowerCase()) return if value.length == 0 diff --git a/app/coffee/modules/common/wisiwyg.coffee b/app/coffee/modules/common/wisiwyg.coffee index e5b73d05..98871836 100644 --- a/app/coffee/modules/common/wisiwyg.coffee +++ b/app/coffee/modules/common/wisiwyg.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/controllerMixins.coffee b/app/coffee/modules/controllerMixins.coffee index 0dd5b474..4bfda2d1 100644 --- a/app/coffee/modules/controllerMixins.coffee +++ b/app/coffee/modules/controllerMixins.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/events.coffee b/app/coffee/modules/events.coffee index f7acf0bc..aed298b8 100644 --- a/app/coffee/modules/events.coffee +++ b/app/coffee/modules/events.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/feedback.coffee b/app/coffee/modules/feedback.coffee index dda743e4..7cfabd75 100644 --- a/app/coffee/modules/feedback.coffee +++ b/app/coffee/modules/feedback.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/integrations.coffee b/app/coffee/modules/integrations.coffee index 0fde7411..7392054e 100644 --- a/app/coffee/modules/integrations.coffee +++ b/app/coffee/modules/integrations.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/issues.coffee b/app/coffee/modules/issues.coffee index 43543202..9399ca8e 100644 --- a/app/coffee/modules/issues.coffee +++ b/app/coffee/modules/issues.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index 22bcbe9e..a4146c7a 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -279,11 +282,10 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $t issue.status = statusId currentLoading = $loading() - .target($el.find(".level-name")) + .target($el) .start() onSuccess = -> - $confirm.notify("success") $model.$setViewValue(issue) $rootScope.$broadcast("object:updated") currentLoading.finish() @@ -296,7 +298,7 @@ IssueStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $t $repo.save(issue).then(onSuccess, onError) - $el.on "click", ".status-data", (event) -> + $el.on "click", ".js-edit-status", (event) -> event.preventDefault() event.stopPropagation() return if not isEditable() @@ -370,7 +372,6 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $tem .start() onSuccess = -> - $confirm.notify("success") $model.$setViewValue(issue) $rootScope.$broadcast("object:updated") currentLoading.finish() @@ -459,7 +460,6 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, .start() onSuccess = -> - $confirm.notify("success") $model.$setViewValue(issue) $rootScope.$broadcast("object:updated") currentLoading.finish() @@ -548,7 +548,6 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, .start() onSuccess = -> - $confirm.notify("success") $model.$setViewValue(issue) $rootScope.$broadcast("object:updated") currentLoading.finish() diff --git a/app/coffee/modules/issues/lightboxes.coffee b/app/coffee/modules/issues/lightboxes.coffee index 63db96aa..de23b12e 100644 --- a/app/coffee/modules/issues/lightboxes.coffee +++ b/app/coffee/modules/issues/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -29,12 +32,15 @@ module = angular.module("taigaIssues") ## Issue Create Lightbox Directive ############################################################################# -CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) -> +CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading, $q, attachmentsService) -> link = ($scope, $el, $attrs) -> form = $el.find("form").checksley() $scope.issue = {} + $scope.attachments = Immutable.List() $scope.$on "issueform:new", (ctx, project)-> + attachmentsToAdd = Immutable.List() + $el.find(".tag-input").val("") lightboxService.open($el) @@ -52,6 +58,21 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) $scope.$on "$destroy", -> $el.off() + + createAttachments = (obj) -> + promises = _.map attachmentsToAdd.toJS(), (attachment) -> + return attachmentsService.upload(attachment.file, obj.id, $scope.issue.project, 'issue') + + return $q.all(promises) + + attachmentsToAdd = Immutable.List() + + resetAttachments = () -> + attachmentsToAdd = Immutable.List() + + $scope.addAttachment = (attachment) -> + attachmentsToAdd = attachmentsToAdd.push(attachment) + submit = debounce 2000, (event) => event.preventDefault() @@ -64,6 +85,9 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) promise = $repo.create("issues", $scope.issue) + promise.then (data) -> + return createAttachments(data) + promise.then (data) -> currentLoading.finish() $rootscope.$broadcast("issueform:new:success", data) @@ -82,7 +106,7 @@ CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading) return {link:link} -module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading", +module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading", "$q", "tgAttachmentsService", CreateIssueDirective]) diff --git a/app/coffee/modules/issues/list.coffee b/app/coffee/modules/issues/list.coffee index 594d73f4..c811b2e7 100644 --- a/app/coffee/modules/issues/list.coffee +++ b/app/coffee/modules/issues/list.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -775,7 +778,7 @@ module.directive("tgIssueStatusInlineEdition", ["$tgRepo", "$tgTemplate", "$root ## Issue assigned to Directive ############################################################################# -IssueAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) -> +IssueAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) -> template = _.template(""" <%- name %>
<%- name %>
@@ -783,11 +786,15 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) -> link = ($scope, $el, $attrs) -> updateIssue = (issue) -> - ctx = {name: "Unassigned", imgurl: "/" + window._version + "/images/unnamed.png"} + ctx = { + name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"), + imgurl: "/#{window._version}/images/unnamed.png" + } + member = $scope.usersById[issue.assigned_to] if member - ctx.imgurl = member.photo ctx.name = member.full_name_display + ctx.imgurl = member.photo $el.find(".avatar").html(template(ctx)) $el.find(".issue-assignedto").attr('title', ctx.name) @@ -819,5 +826,5 @@ IssueAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) -> return {link: link} -module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope", +module.directive("tgIssueAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate" IssueAssignedToInlineEditionDirective]) diff --git a/app/coffee/modules/kanban.coffee b/app/coffee/modules/kanban.coffee index 398b348c..0f0f48f3 100644 --- a/app/coffee/modules/kanban.coffee +++ b/app/coffee/modules/kanban.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index 4ab4a6b3..f4edca5e 100644 --- a/app/coffee/modules/kanban/main.coffee +++ b/app/coffee/modules/kanban/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -413,7 +416,7 @@ module.directive("tgKanbanArchivedStatusIntro", ["$translate", KanbanArchivedSta ## Kanban User Story Directive ############################################################################# -KanbanUserstoryDirective = ($rootscope, $loading, $rs) -> +KanbanUserstoryDirective = ($rootscope, $loading, $rs, $rs2) -> link = ($scope, $el, $attrs, $model) -> $el.disableSelection() @@ -437,8 +440,9 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) -> us = $model.$modelValue $rs.userstories.getByRef(us.project, us.ref).then (editingUserStory) => - $rootscope.$broadcast("usform:edit", editingUserStory) - currentLoading.finish() + $rs2.attachments.list("us", us.id, us.project).then (attachments) => + $rootscope.$broadcast("usform:edit", editingUserStory, attachments.toJS()) + currentLoading.finish() $scope.getTemplateUrl = () -> if $scope.us.isPlaceholder @@ -455,7 +459,7 @@ KanbanUserstoryDirective = ($rootscope, $loading, $rs) -> require: "ngModel" } -module.directive("tgKanbanUserstory", ["$rootScope", "$tgLoading", "$tgResources", KanbanUserstoryDirective]) +module.directive("tgKanbanUserstory", ["$rootScope", "$tgLoading", "$tgResources", "tgResources", KanbanUserstoryDirective]) ############################################################################# ## Kanban Squish Column Directive @@ -523,7 +527,7 @@ module.directive("tgKanbanWipLimit", KanbanWipLimitDirective) ## Kanban User Directive ############################################################################# -KanbanUserDirective = ($log, $compile) -> +KanbanUserDirective = ($log, $compile, $translate) -> template = _.template("""
class="not-clickable"<% } %>> @@ -551,9 +555,17 @@ KanbanUserDirective = ($log, $compile) -> render = (user) -> if user is undefined - ctx = {name: "Unassigned", imgurl: "/" + window._version + "/images/unnamed.png", clickable: clickable} + ctx = { + name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"), + imgurl: "/#{window._version}/images/unnamed.png", + clickable: clickable + } else - ctx = {name: user.full_name_display, imgurl: user.photo, clickable: clickable} + ctx = { + name: user.full_name_display, + imgurl: user.photo, + clickable: clickable + } html = $compile(template(ctx))($scope) $el.html(html) @@ -584,4 +596,4 @@ KanbanUserDirective = ($log, $compile) -> return {link: link, require:"ngModel"} -module.directive("tgKanbanUserAvatar", ["$log", "$compile", KanbanUserDirective]) +module.directive("tgKanbanUserAvatar", ["$log", "$compile", "$translate", KanbanUserDirective]) diff --git a/app/coffee/modules/kanban/sortable.coffee b/app/coffee/modules/kanban/sortable.coffee index 009e54d6..9051b77e 100644 --- a/app/coffee/modules/kanban/sortable.coffee +++ b/app/coffee/modules/kanban/sortable.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/plugins.coffee b/app/coffee/modules/plugins.coffee index a58aaf2f..b826c25c 100644 --- a/app/coffee/modules/plugins.coffee +++ b/app/coffee/modules/plugins.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/projects.coffee b/app/coffee/modules/projects.coffee index 91a14717..95f3641c 100644 --- a/app/coffee/modules/projects.coffee +++ b/app/coffee/modules/projects.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/projects/lightboxes.coffee b/app/coffee/modules/projects/lightboxes.coffee index 55daf92b..c5561ae8 100644 --- a/app/coffee/modules/projects/lightboxes.coffee +++ b/app/coffee/modules/projects/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/related-tasks.coffee b/app/coffee/modules/related-tasks.coffee index 684df1ec..dcb17811 100644 --- a/app/coffee/modules/related-tasks.coffee +++ b/app/coffee/modules/related-tasks.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -25,6 +28,7 @@ debounce = @.taiga.debounce module = angular.module("taigaRelatedTasks", []) + RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $template, $translate) -> templateView = $template.get("task/related-task-row.html", true) templateEdit = $template.get("task/related-task-row-edit.html", true) @@ -40,7 +44,6 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem promise = $repo.save(task) promise.then => currentLoading.finish() - $confirm.notify("success") $rootscope.$broadcast("related-tasks:update") promise.then null, => @@ -89,7 +92,6 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem promise = $repo.remove(task) promise.then -> askResponse.finish() - $confirm.notify("success") $scope.$emit("related-tasks:delete") promise.then null, -> @@ -111,7 +113,9 @@ RelatedTaskRowDirective = ($repo, $compile, $confirm, $rootscope, $loading, $tem return {link:link, require:"ngModel"} -module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", "$tgTemplate", "$translate", RelatedTaskRowDirective]) +module.directive("tgRelatedTaskRow", ["$tgRepo", "$compile", "$tgConfirm", "$rootScope", "$tgLoading", + "$tgTemplate", "$translate", RelatedTaskRowDirective]) + RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $analytics, $template) -> template = $template.get("task/related-task-create-form.html", true) @@ -138,7 +142,6 @@ RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $analytics.trackEvent("task", "create", "create task on userstory", 1) currentLoading.finish() $scope.$emit("related-tasks:add") - $confirm.notify("success") promise.then null, -> $el.find('input').val(task.subject) @@ -188,12 +191,13 @@ RelatedTaskCreateFormDirective = ($repo, $compile, $confirm, $tgmodel, $loading, $el.off() return {link: link} -module.directive("tgRelatedTaskCreateForm", ["$tgRepo", "$compile", "$tgConfirm", "$tgModel", "$tgLoading", "$tgAnalytics", "$tgTemplate", RelatedTaskCreateFormDirective]) -RelatedTaskCreateButtonDirective = ($repo, $compile, $confirm, $tgmodel) -> - template = _.template(""" - - """) +module.directive("tgRelatedTaskCreateForm", ["$tgRepo", "$compile", "$tgConfirm", "$tgModel", "$tgLoading", + "$tgAnalytics", "$tgTemplate", RelatedTaskCreateFormDirective]) + + +RelatedTaskCreateButtonDirective = ($repo, $compile, $confirm, $tgmodel, $template) -> + template = $template.get("common/components/add-button.html", true) link = ($scope, $el, $attrs) -> $scope.$watch "project", (val) -> @@ -204,14 +208,17 @@ RelatedTaskCreateButtonDirective = ($repo, $compile, $confirm, $tgmodel) -> else $el.html("") - $el.on "click", ".icon", (event)-> + $el.on "click", ".add-button", (event)-> $scope.$emit("related-tasks:add-new-clicked") $scope.$on "$destroy", -> $el.off() return {link: link} -module.directive("tgRelatedTaskCreateButton", ["$tgRepo", "$compile", "$tgConfirm", "$tgModel", RelatedTaskCreateButtonDirective]) + +module.directive("tgRelatedTaskCreateButton", ["$tgRepo", "$compile", "$tgConfirm", "$tgModel", + "$tgTemplate", RelatedTaskCreateButtonDirective]) + RelatedTasksDirective = ($repo, $rs, $rootscope) -> link = ($scope, $el, $attrs) -> @@ -238,9 +245,11 @@ RelatedTasksDirective = ($repo, $rs, $rootscope) -> $el.off() return {link: link} + module.directive("tgRelatedTasks", ["$tgRepo", "$tgResources", "$rootScope", RelatedTasksDirective]) -RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService) -> + +RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, $translate) -> template = _.template(""" <%- name %>
<%- name %>
@@ -248,7 +257,10 @@ RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService link = ($scope, $el, $attrs) -> updateRelatedTask = (task) -> - ctx = {name: "Unassigned", imgurl: "/" + window._version + "/images/unnamed.png"} + ctx = { + name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"), + imgurl: "/" + window._version + "/images/unnamed.png" + } member = $scope.usersById[task.assigned_to] if member ctx.imgurl = member.photo @@ -286,4 +298,5 @@ RelatedTaskAssignedToInlineEditionDirective = ($repo, $rootscope, popoverService return {link: link} -module.directive("tgRelatedTaskAssignedToInlineEdition", ["$tgRepo", "$rootScope", RelatedTaskAssignedToInlineEditionDirective]) +module.directive("tgRelatedTaskAssignedToInlineEdition", ["$tgRepo", "$rootScope", "$translate", + RelatedTaskAssignedToInlineEditionDirective]) diff --git a/app/coffee/modules/resources.coffee b/app/coffee/modules/resources.coffee index b6fcb827..fda8262b 100644 --- a/app/coffee/modules/resources.coffee +++ b/app/coffee/modules/resources.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -172,6 +175,9 @@ urls = { # Application tokens "applications": "/applications" "application-tokens": "/application-tokens" + + # Stats + "stats-discover": "/stats/discover" } # Initialize api urls service @@ -210,7 +216,6 @@ module.run([ "$tgIssuesResourcesProvider", "$tgWikiResourcesProvider", "$tgSearchResourcesProvider", - "$tgAttachmentsResourcesProvider", "$tgMdRenderResourcesProvider", "$tgHistoryResourcesProvider", "$tgKanbanResourcesProvider", diff --git a/app/coffee/modules/resources/custom-attributes-values.coffee b/app/coffee/modules/resources/custom-attributes-values.coffee index b3169967..f5a38b2c 100644 --- a/app/coffee/modules/resources/custom-attributes-values.coffee +++ b/app/coffee/modules/resources/custom-attributes-values.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/custom-attributes.coffee b/app/coffee/modules/resources/custom-attributes.coffee index 0d4e934e..520ec2d2 100644 --- a/app/coffee/modules/resources/custom-attributes.coffee +++ b/app/coffee/modules/resources/custom-attributes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/history.coffee b/app/coffee/modules/resources/history.coffee index fd45579a..5ea6886b 100644 --- a/app/coffee/modules/resources/history.coffee +++ b/app/coffee/modules/resources/history.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/invitations.coffee b/app/coffee/modules/resources/invitations.coffee index 49455cf5..50058426 100644 --- a/app/coffee/modules/resources/invitations.coffee +++ b/app/coffee/modules/resources/invitations.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/issues.coffee b/app/coffee/modules/resources/issues.coffee index ebf2dbdb..1568b035 100644 --- a/app/coffee/modules/resources/issues.coffee +++ b/app/coffee/modules/resources/issues.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/kanban.coffee b/app/coffee/modules/resources/kanban.coffee index bdddce74..a79bee06 100644 --- a/app/coffee/modules/resources/kanban.coffee +++ b/app/coffee/modules/resources/kanban.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/locales.coffee b/app/coffee/modules/resources/locales.coffee index 479b7655..a53090f5 100644 --- a/app/coffee/modules/resources/locales.coffee +++ b/app/coffee/modules/resources/locales.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/mdrender.coffee b/app/coffee/modules/resources/mdrender.coffee index 2a40dfc2..98017b49 100644 --- a/app/coffee/modules/resources/mdrender.coffee +++ b/app/coffee/modules/resources/mdrender.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/memberships.coffee b/app/coffee/modules/resources/memberships.coffee index 3de38612..ab7891e5 100644 --- a/app/coffee/modules/resources/memberships.coffee +++ b/app/coffee/modules/resources/memberships.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/notify-policies.coffee b/app/coffee/modules/resources/notify-policies.coffee index 03144893..34abf634 100644 --- a/app/coffee/modules/resources/notify-policies.coffee +++ b/app/coffee/modules/resources/notify-policies.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/projects.coffee b/app/coffee/modules/resources/projects.coffee index 8cc3fabd..a1745420 100644 --- a/app/coffee/modules/resources/projects.coffee +++ b/app/coffee/modules/resources/projects.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -150,6 +153,31 @@ resourceProvider = ($config, $repo, $http, $urls, $auth, $q, $translate) -> return defered.promise + service.changeLogo = (projectId, file) -> + maxFileSize = $config.get("maxUploadFileSize", null) + if maxFileSize and file.size > maxFileSize + response = { + status: 413, + data: _error_message: "'#{file.name}' (#{sizeFormat(file.size)}) is too heavy for our oompa + loompas, try it with a smaller than (#{sizeFormat(maxFileSize)})" + } + defered = $q.defer() + defered.reject(response) + return defered.promise + + data = new FormData() + data.append('logo', file) + options = { + transformRequest: angular.identity, + headers: {'Content-Type': undefined} + } + url = "#{$urls.resolve("projects")}/#{projectId}/change_logo" + return $http.post(url, data, {}, options) + + service.removeLogo = (projectId) -> + url = "#{$urls.resolve("projects")}/#{projectId}/remove_logo" + return $http.post(url) + return (instance) -> instance.projects = service diff --git a/app/coffee/modules/resources/roles.coffee b/app/coffee/modules/resources/roles.coffee index e6d79b8d..f878eda8 100644 --- a/app/coffee/modules/resources/roles.coffee +++ b/app/coffee/modules/resources/roles.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/search.coffee b/app/coffee/modules/resources/search.coffee index ed571e32..c46bbe33 100644 --- a/app/coffee/modules/resources/search.coffee +++ b/app/coffee/modules/resources/search.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/sprints.coffee b/app/coffee/modules/resources/sprints.coffee index 45cff81e..06fadbe1 100644 --- a/app/coffee/modules/resources/sprints.coffee +++ b/app/coffee/modules/resources/sprints.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/tasks.coffee b/app/coffee/modules/resources/tasks.coffee index 64a73b27..6a838a5d 100644 --- a/app/coffee/modules/resources/tasks.coffee +++ b/app/coffee/modules/resources/tasks.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/user-settings.coffee b/app/coffee/modules/resources/user-settings.coffee index 80595b00..ee53c6ef 100644 --- a/app/coffee/modules/resources/user-settings.coffee +++ b/app/coffee/modules/resources/user-settings.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/users.coffee b/app/coffee/modules/resources/users.coffee index 15a5aedb..6d139693 100644 --- a/app/coffee/modules/resources/users.coffee +++ b/app/coffee/modules/resources/users.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/userstories.coffee b/app/coffee/modules/resources/userstories.coffee index 5d624a04..ca40da1e 100644 --- a/app/coffee/modules/resources/userstories.coffee +++ b/app/coffee/modules/resources/userstories.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/resources/wiki.coffee b/app/coffee/modules/resources/wiki.coffee index 8e450745..c333d9cb 100644 --- a/app/coffee/modules/resources/wiki.coffee +++ b/app/coffee/modules/resources/wiki.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/search.coffee b/app/coffee/modules/search.coffee index 022563b4..1c154887 100644 --- a/app/coffee/modules/search.coffee +++ b/app/coffee/modules/search.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -174,7 +177,7 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) -> linkTable = ($scope, $el, $attrs, $ctrl) -> applyAutoTab = true activeSectionName = "userstories" - tabsDom = $el.find("section.search-filter") + tabsDom = $el.find(".search-filter") lastSearchResults = null getActiveSection = (data) -> @@ -197,7 +200,7 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) -> maxVal = value.length selectedSection.name = name selectedSection.value = value - break; + break if maxVal == 0 return selectedSection @@ -206,7 +209,7 @@ SearchDirective = ($log, $compile, $templatecache, $routeparams, $location) -> renderFilterTabs = (data) -> for name, value of data - continue if name == "count" + tabsDom.find("li.#{name}").show() tabsDom.find("li.#{name} .num").html(value.length) markSectionTabActive = (section) -> diff --git a/app/coffee/modules/taskboard.coffee b/app/coffee/modules/taskboard.coffee index 0e136de5..ac09372a 100644 --- a/app/coffee/modules/taskboard.coffee +++ b/app/coffee/modules/taskboard.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/taskboard/charts.coffee b/app/coffee/modules/taskboard/charts.coffee index 517dfc8a..9e6af226 100644 --- a/app/coffee/modules/taskboard/charts.coffee +++ b/app/coffee/modules/taskboard/charts.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/taskboard/lightboxes.coffee b/app/coffee/modules/taskboard/lightboxes.coffee index e850a3d4..9cc979d2 100644 --- a/app/coffee/modules/taskboard/lightboxes.coffee +++ b/app/coffee/modules/taskboard/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -23,10 +26,36 @@ taiga = @.taiga bindOnce = @.taiga.bindOnce debounce = @.taiga.debounce -CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService, $translate) -> +CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService, $translate, $q, attachmentsService) -> link = ($scope, $el, attrs) -> $scope.isNew = true + attachmentsToAdd = Immutable.List() + attachmentsToDelete = Immutable.List() + + resetAttachments = () -> + attachmentsToAdd = Immutable.List() + attachmentsToDelete = Immutable.List() + + $scope.addAttachment = (attachment) -> + attachmentsToAdd = attachmentsToAdd.push(attachment) + + $scope.deleteAttachment = (attachment) -> + attachmentsToDelete = attachmentsToDelete.push(attachment) + + createAttachments = (obj) -> + promises = _.map attachmentsToAdd.toJS(), (attachment) -> + attachmentsService.upload(attachment.file, obj.id, $scope.task.project, 'task') + + return $q.all(promises) + + deleteAttachments = (obj) -> + console.log attachmentsToDelete.toJS() + promises = _.map attachmentsToDelete.toJS(), (attachment) -> + return attachmentsService.delete("task", attachment.id) + + return $q.all(promises) + $scope.$on "taskform:new", (ctx, sprintId, usId) -> $scope.task = { project: $scope.projectId @@ -38,6 +67,9 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer tags: [] } $scope.isNew = true + $scope.attachments = Immutable.List() + + resetAttachments() # Update texts for creation create = $translate.instant("COMMON.CREATE") @@ -49,10 +81,14 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer $el.find(".tag-input").val("") lightboxService.open($el) - $scope.$on "taskform:edit", (ctx, task) -> + $scope.$on "taskform:edit", (ctx, task, attachments) -> $scope.task = task $scope.isNew = false + $scope.attachments = Immutable.fromJS(attachments) + + resetAttachments() + # Update texts for edition save = $translate.instant("COMMON.SAVE") edit = $translate.instant("LIGHTBOX.CREATE_EDIT_TASK.ACTION_EDIT") @@ -80,6 +116,12 @@ CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxSer promise = $repo.save($scope.task) broadcastEvent = "taskform:edit:success" + promise.then (data) -> + createAttachments(data) + deleteAttachments(data) + + return data + currentLoading = $loading() .target(submitButton) .start() @@ -152,7 +194,9 @@ module.directive("tgLbCreateEditTask", [ "$rootScope", "$tgLoading", "lightboxService", - "$translate" + "$translate", + "$q", + "tgAttachmentsService", CreateEditTaskDirective ]) diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index f3491c66..757a0320 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -304,7 +307,7 @@ module.directive("tgTaskboard", ["$rootScope", TaskboardDirective]) ## Taskboard Task Directive ############################################################################# -TaskboardTaskDirective = ($rootscope, $loading, $rs) -> +TaskboardTaskDirective = ($rootscope, $loading, $rs, $rs2) -> link = ($scope, $el, $attrs, $model) -> $el.disableSelection() @@ -327,14 +330,16 @@ TaskboardTaskDirective = ($rootscope, $loading, $rs) -> .start() task = $scope.task + $rs.tasks.getByRef(task.project, task.ref).then (editingTask) => - $rootscope.$broadcast("taskform:edit", editingTask) - currentLoading.finish() + $rs2.attachments.list("task", editingTask.id, editingTask.project).then (attachments) => + $rootscope.$broadcast("taskform:edit", editingTask, attachments.toJS()) + currentLoading.finish() return {link:link} -module.directive("tgTaskboardTask", ["$rootScope", "$tgLoading", "$tgResources", TaskboardTaskDirective]) +module.directive("tgTaskboardTask", ["$rootScope", "$tgLoading", "$tgResources", "tgResources", TaskboardTaskDirective]) ############################################################################# ## Taskboard Squish Column Directive @@ -431,7 +436,7 @@ module.directive("tgTaskboardSquishColumn", ["$tgResources", TaskboardSquishColu ## Taskboard User Directive ############################################################################# -TaskboardUserDirective = ($log) -> +TaskboardUserDirective = ($log, $translate) -> clickable = false link = ($scope, $el, $attrs) -> @@ -442,9 +447,17 @@ TaskboardUserDirective = ($log) -> user = $scope.usersById[assigned_to] if user is undefined - _.assign($scope, {name: "Unassigned", imgurl: "/" + window._version + "/images/unnamed.png", clickable: clickable}) + _.assign($scope, { + name: $translate.instant("COMMON.ASSIGNED_TO.NOT_ASSIGNED"), + imgurl: "/#{window._version}/images/unnamed.png", + clickable: clickable + }) else - _.assign($scope, {name: user.full_name_display, imgurl: user.photo, clickable: clickable}) + _.assign($scope, { + name: user.full_name_display, + imgurl: user.photo, + clickable: clickable + }) username_label.text($scope.name) @@ -479,4 +492,4 @@ TaskboardUserDirective = ($log) -> } -module.directive("tgTaskboardUserAvatar", ["$log", TaskboardUserDirective]) +module.directive("tgTaskboardUserAvatar", ["$log", "$translate", TaskboardUserDirective]) diff --git a/app/coffee/modules/taskboard/sortable.coffee b/app/coffee/modules/taskboard/sortable.coffee index aa4c6ca0..19775335 100644 --- a/app/coffee/modules/taskboard/sortable.coffee +++ b/app/coffee/modules/taskboard/sortable.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/tasks.coffee b/app/coffee/modules/tasks.coffee index 1b267470..2062317d 100644 --- a/app/coffee/modules/tasks.coffee +++ b/app/coffee/modules/tasks.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/tasks/detail.coffee b/app/coffee/modules/tasks/detail.coffee index 96bb59d7..b0c8f688 100644 --- a/app/coffee/modules/tasks/detail.coffee +++ b/app/coffee/modules/tasks/detail.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -275,12 +278,11 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co task.status = status currentLoading = $loading() - .target($el.find(".level-name")) + .target($el) .start() onSuccess = -> $model.$setViewValue(task) - $confirm.notify("success") $rootScope.$broadcast("object:updated") currentLoading.finish() @@ -290,7 +292,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co $repo.save(task).then(onSuccess, onError) - $el.on "click", ".status-data", (event) -> + $el.on "click", ".js-edit-status", (event) -> event.preventDefault() event.stopPropagation() return if not isEditable() @@ -324,17 +326,8 @@ module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", " "$compile", "$translate", "$tgTemplate", TaskStatusButtonDirective]) -TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile) -> - template = _.template(""" -
- - -
- """) +TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile, $template) -> + template = $template.get("issue/iocaine-button.html", true) link = ($scope, $el, $attrs, $model) -> isEditable = -> @@ -364,7 +357,6 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue promise.then -> $model.$setViewValue(task) - $confirm.notify("success") $rootscope.$broadcast("object:updated") promise.then null, -> @@ -392,4 +384,4 @@ TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue } module.directive("tgTaskIsIocaineButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue", - "$compile", TaskIsIocaineButtonDirective]) + "$compile", "$tgTemplate", TaskIsIocaineButtonDirective]) diff --git a/app/coffee/modules/team.coffee b/app/coffee/modules/team.coffee index f67c6b08..8e36614a 100644 --- a/app/coffee/modules/team.coffee +++ b/app/coffee/modules/team.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/team/main.coffee b/app/coffee/modules/team/main.coffee index e134a7e4..2dd37f04 100644 --- a/app/coffee/modules/team/main.coffee +++ b/app/coffee/modules/team/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/user-settings.coffee b/app/coffee/modules/user-settings.coffee index a7cc586c..1a581ed3 100644 --- a/app/coffee/modules/user-settings.coffee +++ b/app/coffee/modules/user-settings.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/user-settings/change-password.coffee b/app/coffee/modules/user-settings/change-password.coffee index e86966b8..4f38b575 100644 --- a/app/coffee/modules/user-settings/change-password.coffee +++ b/app/coffee/modules/user-settings/change-password.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/user-settings/lightboxes.coffee b/app/coffee/modules/user-settings/lightboxes.coffee index 1332baf0..4906c7e9 100644 --- a/app/coffee/modules/user-settings/lightboxes.coffee +++ b/app/coffee/modules/user-settings/lightboxes.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/user-settings/main.coffee b/app/coffee/modules/user-settings/main.coffee index 24424112..98348150 100644 --- a/app/coffee/modules/user-settings/main.coffee +++ b/app/coffee/modules/user-settings/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -145,12 +148,12 @@ UserAvatarDirective = ($auth, $model, $rs, $confirm) -> $auth.setUser(user) $scope.user = user - $el.find('.overlay').addClass('hidden') + $el.find('.loading-overlay').removeClass('active') $confirm.notify('success') onError = (response) -> showSizeInfo() if response.status == 413 - $el.find('.overlay').addClass('hidden') + $el.find('.loading-overlay').removeClass('active') $confirm.notify('error', response.data._error_message) # Change photo @@ -159,12 +162,12 @@ UserAvatarDirective = ($auth, $model, $rs, $confirm) -> $el.on "change", "#avatar-field", (event) -> if $scope.avatarAttachment - $el.find('.overlay').removeClass('hidden') + $el.find('.loading-overlay').addClass("active") $rs.userSettings.changeAvatar($scope.avatarAttachment).then(onSuccess, onError) # Use gravatar photo - $el.on "click", "a.use-gravatar", (event) -> - $el.find('.overlay').removeClass('hidden') + $el.on "click", "a.js-use-gravatar", (event) -> + $el.find('.loading-overlay').addClass("active") $rs.userSettings.removeAvatar().then(onSuccess, onError) $scope.$on "$destroy", -> diff --git a/app/coffee/modules/user-settings/nav.coffee b/app/coffee/modules/user-settings/nav.coffee index 1f820827..f9762b4a 100644 --- a/app/coffee/modules/user-settings/nav.coffee +++ b/app/coffee/modules/user-settings/nav.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/user-settings/notifications.coffee b/app/coffee/modules/user-settings/notifications.coffee index 09ba4152..c089c542 100644 --- a/app/coffee/modules/user-settings/notifications.coffee +++ b/app/coffee/modules/user-settings/notifications.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/userstories.coffee b/app/coffee/modules/userstories.coffee index e0ae0878..ae0df58f 100644 --- a/app/coffee/modules/userstories.coffee +++ b/app/coffee/modules/userstories.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index fb25ed57..08468dc8 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -274,54 +277,6 @@ UsStatusDisplayDirective = ($template, $compile) -> module.directive("tgUsStatusDisplay", ["$tgTemplate", "$compile", UsStatusDisplayDirective]) - -############################################################################# -## User story related tasts progress splay Directive -############################################################################# - -UsTasksProgressDisplayDirective = ($template, $compile) -> - # Display a progress bar with the stats of completed tasks. - # - # Example: - # tg-us-tasks-progress-display(ng-model="tasks") - # - # Requirements: - # - Task object list (ng-model) - # - scope.taskStatusById object - - link = ($scope, $el, $attrs) -> - render = (tasks) -> - totalTasks = tasks.length - totalClosedTasks = _.filter(tasks, (task) => $scope.taskStatusById[task.status].is_closed).length - - progress = if totalTasks > 0 then 100 * totalClosedTasks / totalTasks else 0 - - _.assign($scope, { - totalTasks: totalTasks - totalClosedTasks: totalClosedTasks - progress: progress, - style: { - width: progress + "%" - } - }) - - $scope.$watch $attrs.ngModel, (tasks) -> - render(tasks) if tasks? - - $scope.$on "$destroy", -> - $el.off() - - return { - templateUrl: "us/us-task-progress.html" - link: link - restrict: "EA" - require: "ngModel" - scope: true - } - -module.directive("tgUsTasksProgressDisplay", ["$tgTemplate", "$compile", UsTasksProgressDisplayDirective]) - - ############################################################################# ## User story status button directive ############################################################################# @@ -362,11 +317,10 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $temp $.fn.popover().closeAll() currentLoading = $loading() - .target($el.find(".level-name")) + .target($el) .start() onSuccess = -> - $confirm.notify("success") $model.$setViewValue(us) $rootScope.$broadcast("object:updated") currentLoading.finish() @@ -377,7 +331,7 @@ UsStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $temp $repo.save(us).then(onSuccess, onError) - $el.on "click", ".status-data", (event) -> + $el.on "click", ".js-edit-status", (event) -> event.preventDefault() event.stopPropagation() return if not isEditable() @@ -422,10 +376,6 @@ UsTeamRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qq return $scope.project.my_permissions.indexOf("modify_us") != -1 render = (us) -> - if not canEdit() and not us.team_requirement - $el.html("") - return - ctx = { canEdit: canEdit() isRequired: us.team_requirement @@ -486,10 +436,6 @@ UsClientRequirementButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $ return $scope.project.my_permissions.indexOf("modify_us") != -1 render = (us) -> - if not canEdit() and not us.client_requirement - $el.html("") - return - ctx = { canEdit: canEdit() isRequired: us.client_requirement diff --git a/app/coffee/modules/wiki.coffee b/app/coffee/modules/wiki.coffee index 196c99bb..cf963020 100644 --- a/app/coffee/modules/wiki.coffee +++ b/app/coffee/modules/wiki.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/wiki/main.coffee b/app/coffee/modules/wiki/main.coffee index 86d4901c..88dd33e1 100644 --- a/app/coffee/modules/wiki/main.coffee +++ b/app/coffee/modules/wiki/main.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/modules/wiki/nav.coffee b/app/coffee/modules/wiki/nav.coffee index ead6f803..23935cbb 100644 --- a/app/coffee/modules/wiki/nav.coffee +++ b/app/coffee/modules/wiki/nav.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/coffee/utils.coffee b/app/coffee/utils.coffee index 11abb7fd..6ced2cc5 100644 --- a/app/coffee/utils.coffee +++ b/app/coffee/utils.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -192,6 +195,29 @@ _.mixin delete obj[key]; obj , obj).value() + cartesianProduct: -> + _.reduceRight( + arguments, (a,b) -> + _.flatten(_.map(a, (x) -> _.map b, (y) -> [y].concat(x)), true) + , [ [] ]) + + + +isImage = (name) -> + return name.match(/\.(jpe?g|png|gif|gifv|webm)/i) != null + +patch = (oldImmutable, newImmutable) -> + pathObj = {} + + newImmutable.forEach (newValue, key) -> + if newValue != oldImmutable.get(key) + if newValue.toJS + pathObj[key] = newValue.toJS() + else + pathObj[key] = newValue + + return pathObj + taiga = @.taiga taiga.nl2br = nl2br taiga.bindMethods = bindMethods @@ -215,3 +241,5 @@ taiga.sizeFormat = sizeFormat taiga.stripTags = stripTags taiga.replaceTags = replaceTags taiga.defineImmutableProperty = defineImmutableProperty +taiga.isImage = isImage +taiga.patch = patch diff --git a/app/images/attachment-gallery.png b/app/images/attachment-gallery.png new file mode 100644 index 00000000..b8189c44 Binary files /dev/null and b/app/images/attachment-gallery.png differ diff --git a/app/images/discover.png b/app/images/discover.png new file mode 100644 index 00000000..04568fc6 Binary files /dev/null and b/app/images/discover.png differ diff --git a/app/images/looking-for-people.png b/app/images/looking-for-people.png new file mode 100644 index 00000000..89800164 Binary files /dev/null and b/app/images/looking-for-people.png differ diff --git a/app/images/project-logos/project-logo-01.png b/app/images/project-logos/project-logo-01.png new file mode 100644 index 00000000..f8491702 Binary files /dev/null and b/app/images/project-logos/project-logo-01.png differ diff --git a/app/images/project-logos/project-logo-02.png b/app/images/project-logos/project-logo-02.png new file mode 100644 index 00000000..c4034369 Binary files /dev/null and b/app/images/project-logos/project-logo-02.png differ diff --git a/app/images/project-logos/project-logo-03.png b/app/images/project-logos/project-logo-03.png new file mode 100644 index 00000000..c3f2f833 Binary files /dev/null and b/app/images/project-logos/project-logo-03.png differ diff --git a/app/images/project-logos/project-logo-04.png b/app/images/project-logos/project-logo-04.png new file mode 100644 index 00000000..a33c622e Binary files /dev/null and b/app/images/project-logos/project-logo-04.png differ diff --git a/app/images/project-logos/project-logo-05.png b/app/images/project-logos/project-logo-05.png new file mode 100644 index 00000000..5f5afd0a Binary files /dev/null and b/app/images/project-logos/project-logo-05.png differ diff --git a/app/index.jade b/app/index.jade index b53656f9..71ac29b6 100644 --- a/app/index.jade +++ b/app/index.jade @@ -9,6 +9,8 @@ html(lang="en") title Taiga meta(name="description", content="Taiga is a project management platform for startups and agile developers & designers who want a simple, beautiful tool that makes work truly enjoyable.") meta(name="keywords", content="agile, scrum, taiga, management, project, developer, designer, user experience") + meta(http-equiv="X-Frame-Options", content="deny") + //-meta(name="viewport", content="width=device-width, user-scalable=no") link(rel="stylesheet", href="/#{v}/styles/theme-taiga.css") link(rel="icon", type="image/png", href="/#{v}/images/favicon.png") diff --git a/app/js/murmurhash3_gc.js b/app/js/murmurhash3_gc.js new file mode 100644 index 00000000..57e0dfb6 --- /dev/null +++ b/app/js/murmurhash3_gc.js @@ -0,0 +1,89 @@ +/** + * JS Implementation of MurmurHash3 (r136) (as of May 20, 2011) + * + * Copyright (c) 2011 Gary Court + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * @author Gary Court + * @see http://github.com/garycourt/murmurhash-js + * @author Austin Appleby + * @see http://sites.google.com/site/murmurhash/ + * + * @param {string} key ASCII only + * @param {number} seed Positive integer only + * @return {number} 32-bit positive integer hash + */ + +function murmurhash3_32_gc(key, seed) { + var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i; + + remainder = key.length & 3; // key.length % 4 + bytes = key.length - remainder; + h1 = seed; + c1 = 0xcc9e2d51; + c2 = 0x1b873593; + i = 0; + + while (i < bytes) { + k1 = + ((key.charCodeAt(i) & 0xff)) | + ((key.charCodeAt(++i) & 0xff) << 8) | + ((key.charCodeAt(++i) & 0xff) << 16) | + ((key.charCodeAt(++i) & 0xff) << 24); + ++i; + + k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); + h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; + h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); + } + + k1 = 0; + + switch (remainder) { + case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; + case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; + case 1: k1 ^= (key.charCodeAt(i) & 0xff); + + k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; + h1 ^= k1; + } + + h1 ^= key.length; + + h1 ^= h1 >>> 16; + h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; + h1 ^= h1 >>> 13; + h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; + h1 ^= h1 >>> 16; + + return h1 >>> 0; +} + + diff --git a/app/locales/taiga/locale-ca.json b/app/locales/taiga/locale-ca.json index 488acfb1..0f324f4c 100644 --- a/app/locales/taiga/locale-ca.json +++ b/app/locales/taiga/locale-ca.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Si", "NO": "No", + "OR": "or", "LOADING": "Carregant...", "LOADING_PROJECT": "Carregant projecte...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "La teua eïna de gestió de projectes àgil, gratuita i de codi obert ", "TAG_LINE_2": "ESTIMA EL TEU PROJECTE", "BLOCK": "Bloquejar", - "UNBLOCK": "Desbloquejar", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Bloquejat", + "UNBLOCK": "Desbloquejar", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Per favor, explica la raó", "CREATED_BY": "Creat per {{fullDisplayName}}", "FROM": "de", "TO": "a", "CLOSE": "Tancar", - "BLOCKED_NOTE": "Per qué està bloquejada esta tasca?", - "BLOCKED_REASON": "Per favor, explica la raó", "GO_HOME": "Porta'm a l'inici ", "PLUGINS": "Plugins", "BETA": "Estem en beta!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "un usuari extern", "GENERIC_ERROR": "Un Oompa Loompas diu {{error}}.", "IOCAINE_TEXT": "Un poc saturat per una tasca? Fes-ho saber als teus companys clicant a Iocaina quan edites la tasca. Es possible ser inmune a aquesta (fictícia) poció mortal consumint xicotetes dòsis poc a poc, així com es possible millorar amb xicotets nous desafiaments!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Be careful! You're writing with capital letters and this input is case sensitive.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Aquest valor pareix invàlid.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Sense assignar", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Esborrar assignament", "REMOVE_ASSIGNED": "Esborra assignat", "TOO_MANY": ".. massa usuaris, segueix filtrant", "CONFIRM_UNASSIGNED": "Segut que vols deixar-ho sense assignar?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Editar assignament" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Editar assignament", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Tancat", "OPEN": "Obert" }, "WATCHERS": { + "WATCHERS": "Watchers", "ADD": "Add watchers", "TITLE_ADD": "Add a project member to the watchers list", "DELETE": "Esborrar seguidor", @@ -176,7 +184,7 @@ "SAVE": "Salva camp personalitzat", "EDIT": "Edita el camp personalitzat", "DELETE": "Esborrar atribut personalitzat", - "CONFIRM_DELETE": "Recorda que tots els valors a aquest camp personalitzat seràn esborrats.
Segur que voleu continuar? " + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "filtres", @@ -274,9 +282,9 @@ "HEADER": "Ja tinc un compte de Taiga", "PLACEHOLDER_AUTH_NAME": "Nom d'usuari i correu electrònic (sensible a majúscules i minúscules)", "LINK_FORGOT_PASSWORD": "L'has oblidat?", - "TITLE_LINK_FORGOT_PASSWORD": "Has oblidat la teva contrasenya?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?", "ACTION_ENTER": "Entrar", - "ACTION_SIGN_IN": "Entrar", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Contrasenya (sensible a majúscules i minúscules)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Home - Taiga", "PAGE_DESCRIPTION": "La home de Taiga amb els teus projectes principals, totes les històries d'usuari, tasques i incidències que tens assignades i aquelles que estàs Watching", "EMPTY_WORKING_ON": "Sembla buit, no? Comenceu a treballar amb Taiga i voràs ací les históries, tasques e incidències en les que estás treballant.", - "EMPTY_WATCHING": "Segueix les históries, tasques e incidéncies...que vols tindre en compte :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "No tens cap projecte encara", "WORKING_ON_SECTION": "En Progrés", - "WATCHING_SECTION": "Observant" + "WATCHING_SECTION": "Observant", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "Els meus projectes - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "Adjunts", "TITLE": "{{ fileName }} pujat el {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Escriu una descripció curta", "DEPRECATED": "(obsolet)", "DEPRECATED_FILE": "Obsolet?", "ADD": "Afegeix un nou adjunt. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Max. grandària: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ mostra els adjunts obsolets", "HIDE_DEPRECATED": "- Amagar els adjunts obsolets", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Esborrar adjunt...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "l'adjunt '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "No hem pogut esborrar: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) es massa gran per als nostres Oompa Loompas, prova amb algun inferior a ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "es obsolet" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Nombre de punts d'històries (0 per a una quantitat indeterminada)", "TAGS": "Etiquetes", "DESCRIPTION": "Descripció", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Projecte públic", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Projecte privat", - "DELETE": "Esborra aquest projecte" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Esborra aquest projecte", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Informes", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Les persones amb les que treballa a Taiga seran els seus contactes de forma automàtica", "REPORT": "Informar d'un abús", "TABS": { - "ACTIVITY_TAB": "Activitat", + "ACTIVITY_TAB": "Timeline", "ACTIVITY_TAB_TITLE": "Mostra tota la activitat d'aquest usuari", "PROJECTS_TAB": "Projectes", "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projectes", "HELP": "Reordena els teus projectes per a establir els més utilitzats en les primeres posicions.
Els 10 millors projectes apareixeran en la llista de projectes de la barra de navegació superior", "PRIVATE": "Projecte privat", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "punts
projecte", "DEFINED": "punts
definits", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Cerca en...", "ACTION_CREATE_PROJECT": "Crear projecte", "ACTION_IMPORT_PROJECT": "Importar projecte", - "SEE_MORE_PROJECTS": "Visualitza més projectes", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Crear projecte", "TITLE_IMPORT_PROJECT": "Importar projecte", "TITLE_PRVIOUS_PROJECT": "Mostra projectes previs", "TITLE_NEXT_PROJECT": "Mostrar próxims projectes", "HELP_TITLE": "Pàgina d'ajuda de Taiga", "HELP": "Ajuda", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Enviar suggerències", "FEEDBACK": "Suggerències", "NOTIFICATIONS_TITLE": "Edita la configuració de les teves notificacions", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Escriu un nou comentari ací", "SHOW_DELETED": "Mostra el comentari esborrat.", "HIDE_DELETED": "Amaga el comentari esborrat", + "DELETE": "Delete comment", "RESTORE": "Resturar comentari." }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Admin", "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", "MOVE_US_TO_CURRENT_SPRINT": "Envia al Sprint", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Mostra filtres", "SHOW_TAGS": "Mostra etiquetes", "EMPTY": "The backlog is empty!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Anar al panell de {{::name}}", "NUMBER_SPRINTS": "
sprints", "EMPTY": "There are no sprints yet", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", "TITLE_ACTION_NEW_SPRINT": "Add new sprint", "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "La imatge serà escalada a 80x80px.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Canviar", - "ACTION_USE_GRAVATAR": "Utilitza imatge de gravatar", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Esborrar compte de Taiga", "CHANGE_EMAIL_SUCCESS": "Mira el teu correu!
Hem enviat un correu al teu conter
amb les instrucciones per a escriure una nova adreça de correu", "CHANGE_PHOTO": "Canviar foto", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Did you forgot what were you working on?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Don't worry, on your dashboard you'll find your open tasks, issues, and user stories in the order you worked on them." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} s'ha unit a Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "Una aplicació externa requereix autenticació", @@ -1354,6 +1384,12 @@ "CANCEL": "Següent" }, "JOYRIDE": { + "NAV": { + "NEXT": "Següent", + "BACK": "Back", + "SKIP": "Skip", + "DONE": "Done" + }, "DASHBOARD": { "STEP1": { "TITLE": "El teu projecte", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Observant", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Good luck!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Tot", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Escriu algo...", + "ACTION_TITLE": "Cerca", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-de.json b/app/locales/taiga/locale-de.json index f997a059..dbb49707 100644 --- a/app/locales/taiga/locale-de.json +++ b/app/locales/taiga/locale-de.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Ja", "NO": "Nein", + "OR": "or", "LOADING": "Wird geladen...", "LOADING_PROJECT": "Projekt wird geladen...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Dein agiles, freies und quelloffenes Projekt-Management Tool", "TAG_LINE_2": "LIEBE DEIN PROJEKT", "BLOCK": "Blockieren", - "UNBLOCK": "Blockierung aufheben", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Blockiert", + "UNBLOCK": "Blockierung aufheben", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Bitte erklären Sie den Grund ", "CREATED_BY": "Erstellt durch {{fullDisplayName}}", "FROM": "von", "TO": "zu", "CLOSE": "schließen", - "BLOCKED_NOTE": "Was blockiert diese User-Story?", - "BLOCKED_REASON": "Bitte erklären Sie den Grund ", "GO_HOME": "Führe mich heim", "PLUGINS": "Plugins", "BETA": "Wir sind auf beta! ", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "ein externer Benutzer", "GENERIC_ERROR": "Eins unserer Helferlein sagt {{error}}.", "IOCAINE_TEXT": "Fühlen Sie sich von einer Aufgabe etwas erdrückt? Stellen Sie sicher, dass andere davon erfahren, indem Sie auf Locaine klicken, wenn Sie eine Aufgabe ändern. Es ist möglich, gegen dieses (fiktive) tödliche Gift immun zu werden, indem man kleine Mengen über einen Zeitraum hinweg einnimmt. Genauso, wie es möglich ist, besser in dem zu werden, was man tut, indem man gelegentlich zusätzliche Herausforderungen annimmt! ", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Achtung! SIe schreiben in Großbuchstaben und bei dieser Eingabe ist Groß- und Kleinschreibung zu beachten", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Dieser Wert scheint ungültig zu sein. ", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Nicht zugeordnet", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Zuordnung löschen", "REMOVE_ASSIGNED": "Zugewiesene entfernen", "TOO_MANY": "...zu viele Benutzer. Filtern Sie! ", "CONFIRM_UNASSIGNED": "Möchten dies wirklich ohne eine Zuordnung verlassen?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Zuordnungen bearbeiten" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Zuordnungen bearbeiten", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Geschlossen", "OPEN": "Geöffnet" }, "WATCHERS": { + "WATCHERS": "Beobachter", "ADD": "Beobachter hinzufügen", "TITLE_ADD": "Füge ein neues Projektmitglied der Beobachterliste hinzu", "DELETE": "Beobachter löschen", @@ -176,7 +184,7 @@ "SAVE": "Benutzerdefiniertes Feld speichern", "EDIT": "Benutzerdefiniertes Feld bearbeiten", "DELETE": "Benutzerattribut löschen", - "CONFIRM_DELETE": "Denken Sie daran, dass alle Werte in diesem benutzerdefinierten Feld gelöscht werden.
Möchten Sie den Vorgang wirklich fortsetzen?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "Filter", @@ -274,9 +282,9 @@ "HEADER": "Ich bin bereits bei Taiga angemeldet", "PLACEHOLDER_AUTH_NAME": "Benutzername oder E-Mail-Adresse", "LINK_FORGOT_PASSWORD": "Haben Sie es vergessen?", - "TITLE_LINK_FORGOT_PASSWORD": "Hast Du Dein Passwort vergessen?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?", "ACTION_ENTER": "Eingabe", - "ACTION_SIGN_IN": "Anmelden", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Passwort" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Home - Taiga", "PAGE_DESCRIPTION": "Die Taiga Homepage mit Ihren wichtigsten Projekten und all Ihren zugeordneten und beobachteten User-Stories, Aufgaben und Tickets. ", "EMPTY_WORKING_ON": "Hier sieht’s ziemlich leer aus, oder? Beginne deine Arbeit mit Taiga und wir zeigen dir hier die Stories, Tasks und Issues an denen du arbeitest.", - "EMPTY_WATCHING": "Folge User Stories, Tasks, Issues … alles was du wissen musst :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "Sie haben noch keine Projekte", "WORKING_ON_SECTION": "Zuletzt bearbeitet", - "WATCHING_SECTION": "Beobachtet" + "WATCHING_SECTION": "Beobachtet", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "Meine Projekte - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "Anhänge", "TITLE": "{{ fileName }} hochgeladen am {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Geben Sie eine kurze Beschreibung ein", "DEPRECATED": "(verworfen)", "DEPRECATED_FILE": "Verworfen?", "ADD": "Neuen Anhang hinzufügen. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Max. Größe: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ verworfene Anhänge zeigen", "HIDE_DEPRECATED": "- verworfene Anhänge verbergen", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Anhang löschen...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "der Anhang '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Es war uns nicht möglich, zu löschen: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) ist zu schwierig für unsere Helferlein, versuchen Sie es bitte mit einer kleineren Datei als ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "ist verworfen" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Anzahl an US Points (0 für unbegrenzte Anzahl)", "TAGS": "Schlagwörter", "DESCRIPTION": "Beschreibung", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Öffentliches Projekt", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Privates Projekt", - "DELETE": "Dieses Projekt löschen" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Dieses Projekt löschen", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Berichte", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Die Leute, mit denen Sie auf Taiga zusammenarbeiten, werden automatisch zu Ihren Kontakten ", "REPORT": "Missbrauch melden ", "TABS": { - "ACTIVITY_TAB": "Aktivität", + "ACTIVITY_TAB": "Zeitlinie ", "ACTIVITY_TAB_TITLE": "Alle Aktivitäten dieses Benutzers anzeigen", "PROJECTS_TAB": "Projekte", "PROJECTS_TAB_TITLE": "Liste alle Projekte auf, bei denen der Benutzer Mitglied ist", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projekte", "HELP": "Sortieren Sie Ihre Projekte nach Wichtigkeit.
Die ersten 10 Projekte erscheinen prominent in der Projektliste der Navigationsleiste.", "PRIVATE": "Privates Projekt", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{ein Fan} other{# Fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{ein Beobachter} other{# Beobachter}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "Projekt
Punkte", "DEFINED": "definierte
Punkte", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Suchen...", "ACTION_CREATE_PROJECT": "Projekt anlegen", "ACTION_IMPORT_PROJECT": "Projekt importieren", - "SEE_MORE_PROJECTS": "Weitere Projekte ansehen", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Projekt anlegen", "TITLE_IMPORT_PROJECT": "Projekt importieren", "TITLE_PRVIOUS_PROJECT": "Frühere Projekte anzeigen", "TITLE_NEXT_PROJECT": "Weitere Projekte zeigen", "HELP_TITLE": "Taiga Support Seite", "HELP": "Hilfe", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Feedback senden", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Benachrichtigungseinstellungen bearbeiten", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Geben Sie hier einen neuen Kommentar ein", "SHOW_DELETED": "Gelöschten Kommentar anzeigen", "HIDE_DELETED": "Gelöschten Kommentar ausblenden", + "DELETE": "Delete comment", "RESTORE": "Kommentar wiederherstellen" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Administrator ", "CUSTOMIZE_GRAPH_TITLE": "Stelle Points und Sprints über das Adminpanel ein", "MOVE_US_TO_CURRENT_SPRINT": "Zum aktuellen Sprint wechseln", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Filter zeigen", "SHOW_TAGS": "Tags anzeigen", "EMPTY": "Das Backlog ist leer!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Gehe zu Taskboard von \"{{name}}\"", "NUMBER_SPRINTS": "
Sprints", "EMPTY": "Derzeit sind keine Sprints vorhanden", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Ziehe Stories aus deiner Auftragsliste her um einen neuen Sprint zu starten", "TITLE_ACTION_NEW_SPRINT": "Neuen Sprint hinzufügen", "TEXT_ACTION_NEW_SPRINT": "Du möchtest einen neuen Sprint in Deinem Projekt erstellen", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "Das Bild wird auf 80x80px skaliert.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Ändern", - "ACTION_USE_GRAVATAR": "Gravatar verwenden", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Ihr Taiga Benutzerkonto löschen", "CHANGE_EMAIL_SUCCESS": "Sehen Sie in Ihren Posteingang!
Wir haben eine E-Mail an Ihr Konto gesendet
mit der Anleitung, wie Sie Ihre neue Adresse anlegen", "CHANGE_PHOTO": "Foto ändern", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams können nun benutzerdefinierte Felder anlegen, um Werte einzugeben, die für Ihren Workflow wichtig sind.", "HINT3_TITLE": "Sortiere Deine Projekte, um Dir vor allem die anzuzeigen, die relevant für Dich sind.", "HINT3_TEXT": "Die 10 Projekt sind in der Zugriffsleiste am oberen Bildschirmrand aufgelistet. ", - "HINT4_TITLE": "Haben Sie vergessen, woran Sie arbeiten?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Machen Sie sich keine Sorgen, im Dashboard finden Sie Aufgaben, Tickets und User-Stories in der Reihenfolge in der Sie diese bearbeitet haben." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} ist Taiga beigetreten" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Durch Klick auf \"Anmelden\" stimmst du unseren
Nutzungsbedingungen und Datenschutzbestimmungen zu." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "Eine externe Anwendung benötigt eine Genehmigung", @@ -1354,6 +1384,12 @@ "CANCEL": "Abbrechen" }, "JOYRIDE": { + "NAV": { + "NEXT": "Weiter", + "BACK": "Back", + "SKIP": "Skip", + "DONE": "Erledigt" + }, "DASHBOARD": { "STEP1": { "TITLE": "Ihr Projekt", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Beobachtet", - "TEXT1": "Und hier drüben siehst du die, von denen du wissen willst.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "Du arbeitest schon mit Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Viel Glück!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Alle", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Geben sie etwas ein...", + "ACTION_TITLE": "Suche", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index f446c6c9..fdf60757 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Yes", "NO": "No", + "OR": "or", "LOADING": "Loading...", "LOADING_PROJECT": "Loading project...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Your agile, free, and open source project management tool", "TAG_LINE_2": "LOVE YOUR PROJECT", "BLOCK": "Block", - "UNBLOCK": "Unblock", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Blocked", + "UNBLOCK": "Unblock", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Please explain the reason", "CREATED_BY": "Created by {{fullDisplayName}}", "FROM": "from", "TO": "to", "CLOSE": "close", - "BLOCKED_NOTE": "Why is this user story blocked?", - "BLOCKED_REASON": "Please explain the reason", "GO_HOME": "Take me home", "PLUGINS": "Plugins", "BETA": "We are on beta!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "an external user", "GENERIC_ERROR": "One of our Oompa Loompas says {{error}}.", "IOCAINE_TEXT": "Feeling a bit overwhelmed by a task? Make sure others know about it by clicking on Iocaine when editing a task. It's possible to become immune to this (fictional) deadly poison by consuming small amounts over time just as it's possible to get better at what you do by occasionally taking on extra challenges!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Be careful! You're writing with capital letters and this input is case sensitive.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "This value seems to be invalid.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Not assigned", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Delete assignment", "REMOVE_ASSIGNED": "Remove assigned", "TOO_MANY": "...too many users, keep filtering", "CONFIRM_UNASSIGNED": "Are you sure you want to leave it unassigned?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Edit assignment" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Edit assignment", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Closed", "OPEN": "Open" }, "WATCHERS": { + "WATCHERS": "Watchers", "ADD": "Add watchers", "TITLE_ADD": "Add a project member to the watchers list", "DELETE": "Delete watcher", @@ -176,7 +184,7 @@ "SAVE": "Save Custom Field", "EDIT": "Edit Custom Field", "DELETE": "Delete custom attribute", - "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.
Are you sure you want to continue?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "filters", @@ -344,10 +352,11 @@ "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": "It feels empty, doesn't it? Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "You don't have any projects yet", "WORKING_ON_SECTION": "Working on", - "WATCHING_SECTION": "Watching" + "WATCHING_SECTION": "Watching", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "My projects - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "attachments", "TITLE": "{{ fileName }} uploaded on {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Type a short description", "DEPRECATED": "(deprecated)", "DEPRECATED_FILE": "Deprecated?", "ADD": "Add new attachment. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Max. size: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ show deprecated atachments", "HIDE_DEPRECATED": "- hide deprecated atachments", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Delete attachment...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "the attachment '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "We have not been able to delete: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) is too heavy for our Oompa Loompas, try it with a smaller than ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "is deprecated" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)", "TAGS": "Tags", "DESCRIPTION": "Description", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Public project", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Private project", - "DELETE": "Delete this project" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Delete this project", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Reports", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "The people with whom you work at Taiga will be your contacts automatically", "REPORT": "Report Abuse", "TABS": { - "ACTIVITY_TAB": "Activity", + "ACTIVITY_TAB": "Timeline", "ACTIVITY_TAB_TITLE": "Show all the activity of this user", "PROJECTS_TAB": "Projects", "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projects", "HELP": "Reorder your projects to set in the top the most used ones.
The top 10 projects will appear in the top navigation bar project list", "PRIVATE": "Private project", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "project
points", "DEFINED": "defined
points", @@ -734,6 +760,7 @@ "TITLE_NEXT_PROJECT": "Show next projects", "HELP_TITLE": "Taiga Support Page", "HELP": "Help", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Send feedback", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Edit your notification settings", @@ -889,6 +916,7 @@ "TYPE_NEW_COMMENT": "Type a new comment here", "SHOW_DELETED": "Show deleted comment", "HIDE_DELETED": "Hide deleted comment", + "DELETE": "Delete comment", "RESTORE": "Restore comment" }, "ACTIVITY": { @@ -952,6 +980,7 @@ "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", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Show filters", "SHOW_TAGS": "Show tags", "EMPTY": "The backlog is empty!", @@ -1016,6 +1045,7 @@ "TITLE_LINK_TASKBOARD": "Go to Taskboard of \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", "EMPTY": "There are no sprints yet", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", "TITLE_ACTION_NEW_SPRINT": "Add new sprint", "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", @@ -1257,9 +1287,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "The image will be scaled to 80x80px.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Change", - "ACTION_USE_GRAVATAR": "Use gravatar image", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Delete Taiga account", "CHANGE_EMAIL_SUCCESS": "Check your inbox!
We have sent a mail to your account
with the instructions to set your new address", "CHANGE_PHOTO": "Change photo", @@ -1346,7 +1376,7 @@ "NEW_USER": "{{username}} has joined Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "An external app requires authentication", @@ -1374,7 +1404,7 @@ }, "STEP3": { "TITLE": "Watching", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1417,5 +1447,33 @@ "TEXT2": "Good luck!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "All", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Type something...", + "ACTION_TITLE": "Search", + "RESULTS": "Search results" + } } } diff --git a/app/locales/taiga/locale-es.json b/app/locales/taiga/locale-es.json index d49dd3a7..227d8fa8 100644 --- a/app/locales/taiga/locale-es.json +++ b/app/locales/taiga/locale-es.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Sí", "NO": "No", + "OR": "o", "LOADING": "Cargando...", "LOADING_PROJECT": "Cargando proyecto...", "DATE": "DD MMM YYYY", @@ -17,15 +18,17 @@ "DRAG": "Arrastra", "TAG_LINE": "Tu herramienta de gestión de proyecto ágil, gratuíta y de código abierto", "TAG_LINE_2": "AMA TU PROYECTO", - "BLOCK": "Bloqueada", - "UNBLOCK": "Desbloquear", + "BLOCK": "Bloquear", + "BLOCK_TITLE": "Bloquea este elemento por ejemplo si tiene una dependencia que no ha sido satisfecha", "BLOCKED": "Bloqueada", + "UNBLOCK": "Desbloquear", + "UNBLOCK_TITLE": "Desbloquea este elemento", + "BLOCKED_NOTE": "¿Por qué está bloqueada?", + "BLOCKED_REASON": "Por favor, explica la razón", "CREATED_BY": "Creada por {{fullDisplayName}}", "FROM": "de", "TO": "a", "CLOSE": "cerrar", - "BLOCKED_NOTE": "¿Por qué está esta historia de usuario bloqueada?", - "BLOCKED_REASON": "Por favor, explica la razón", "GO_HOME": "Llévame a casa", "PLUGINS": "Plugins", "BETA": "¡Estamos en fase beta!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "un usuario externo", "GENERIC_ERROR": "Uno de nuestros Oompa Loompas dice {{error}}.", "IOCAINE_TEXT": "¿Te sientes fuera de tu zona de confort en una tarea? Asegúrate de que los demás están al tanto de ello, marca el check de la Iocaína al editar una tarea. Igual eu era posible llegar a ser inmune a este veneno mortal a base de consumir pequeñas dosis a lo largo del tiempo, es posible conseguir mejor en lo que estás haciendo si afrontas de vez en cuando esta clase de retos!", + "CLIENT_REQUIREMENT": "Requerimiento de cliente es un nuevo requisito que no se esperaba y es necesario que forme parte del proyecto.", + "TEAM_REQUIREMENT": "Requerimiento del equipo es un nuevo requisito que debe existir en el proyecto pero que no conllevará ningún coste para el cliente.", "CAPSLOCK_WARNING": "¡Ten cuidado! Usted está escribiendo con mayúsculas y esta entrada es sensible a mayúsculas.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Este valor parece inválido.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Sin asignar", + "ASSIGN": "Asignar", "DELETE_ASSIGNMENT": "Eliminar asignaciones", "REMOVE_ASSIGNED": "Eliminar asignación", "TOO_MANY": "...Demasiados usuarios, continué filtrando", "CONFIRM_UNASSIGNED": "¿Está seguro de que desea dejarla sin asignar?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Editar asignación" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Editar asignación", + "SELF": "Asignármela" }, "STATUS": { "CLOSED": "Cerrada", "OPEN": "Abierto" }, "WATCHERS": { + "WATCHERS": "Observadores", "ADD": "Añadir observadores", "TITLE_ADD": "Añade un miembro del proyecto a la lista de observadores", "DELETE": "Eliminar el observador", @@ -176,7 +184,7 @@ "SAVE": "Guardar atributo personalizado", "EDIT": "Editar Atributo Personalizado", "DELETE": "Borrar atributos personalizados", - "CONFIRM_DELETE": "Recuerda que todos los valores en este atributo personalizado serán borrados.
¿Estás seguro que desea continuar?" + "CONFIRM_DELETE": "Se borrarán todos los valores de este atributo personalizado. \n¿Estás seguro de que quieres continuar?" }, "FILTERS": { "TITLE": "filtros", @@ -274,9 +282,9 @@ "HEADER": "Ya tengo una cuenta en Taiga", "PLACEHOLDER_AUTH_NAME": "Nombre de usuario o email (distingue mayúsculas y minúsculas)", "LINK_FORGOT_PASSWORD": "¿La olvidaste?", - "TITLE_LINK_FORGOT_PASSWORD": "¿Has olvidado tu contraseña?", + "TITLE_LINK_FORGOT_PASSWORD": "¿Olvidó su contraseña?", "ACTION_ENTER": "Entrar", - "ACTION_SIGN_IN": "Ingresar", + "ACTION_SIGN_IN": "Iniciar sesión", "PLACEHOLDER_AUTH_PASSWORD": "Contraseña (distingue mayúsculas y minúsculas)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Inicio - Taiga", "PAGE_DESCRIPTION": "Página de inicio de Taiga, con tus proyectos principales y tus historias de usuario, tareas y peticiones en progreso asignadas y las que observas.", "EMPTY_WORKING_ON": "Parece vacío no? Empiece a trabajar con Taiga y verá aquí las historias, las tareas y los incidentes en los que está trabajando.", - "EMPTY_WATCHING": "Sigue Historias de Usuario, tareas, peticiones... sobre las que quieres estar informado :)", + "EMPTY_WATCHING": "Sigue Historias de Usuario, Tareas y Peticiones en tus proyectos y se te notificará sobre sus cambios :)", "EMPTY_PROJECT_LIST": "Todavía no tienes ningún proyecto", "WORKING_ON_SECTION": "Trabajando en", - "WATCHING_SECTION": "Observando" + "WATCHING_SECTION": "Observando", + "DASHBOARD": "Dashboard de proyecto" }, "PROJECTS": { "PAGE_TITLE": "Mis proyectos - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "adjuntos", "TITLE": "{{ fileName }} subido el {{ date }}", + "LIST_VIEW_MODE": "Vista en modo Lista", + "GALLERY_VIEW_MODE": "Vista en modo Galería", "DESCRIPTION": "Escribe una pequeña descripción", "DEPRECATED": "(obsoleto)", "DEPRECATED_FILE": "¿Desactualizado?", "ADD": "Agrega nuevos adjunto. {{maxFileSizeMsg}}", + "DROP": "¡Arrastre los archivos adjuntos aquí!", "MAX_FILE_SIZE": "[Tamaño Max. : {{maxFileSize}}]", "SHOW_DEPRECATED": "+ muestra adjuntos desactualizados", "HIDE_DEPRECATED": "- ocultar adjuntos obsoletos", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Borrar adjunto...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "el adjunto '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "No hemos podido borrarlo: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "El fichero '{{fileName}}' ({{fileSize}}) es demasiado pesado para nuestros Oompa Loompas, prueba con uno de menos de ({{maxFileSize}}).", "FIELDS": { "IS_DEPRECATED": "Está desactualizado" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Número de puntos de historias de usuario (0 para indeterminado)", "TAGS": "Etiquetas", "DESCRIPTION": "Descripción", + "RECRUITING": "¿Este proyecto está buscando a gente?", + "RECRUITING_MESSAGE": "¿Qué estás buscando?", + "RECRUITING_PLACEHOLDER": "Define el perfil de lo que estás buscando", "PUBLIC_PROJECT": "Proyecto público", + "PUBLIC_PROJECT_DESC": "Los usuarios serán capaces de encontrar y ver tu proyecto", "PRIVATE_PROJECT": "Proyecto privado", - "DELETE": "Eliminar este proyecto" + "PRIVATE_PROJECT_DESC": "Este proyecto estará oculto al público por defecto.", + "PRIVATE_OR_PUBLIC": "¿Cuál es la diferencia entre proyecto público y privado?", + "DELETE": "Eliminar este proyecto", + "LOGO_HELP": "La imagen se escalará a 80x80px.", + "CHANGE_LOGO": "Cambia el logo", + "ACTION_USE_DEFAULT_LOGO": "Usar imagen por defecto" }, "REPORTS": { "TITLE": "Informes", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Las personas con las que trabajas en Taiga serán tus contactos automáticamente", "REPORT": "Reportar Abuso", "TABS": { - "ACTIVITY_TAB": "Actividad", + "ACTIVITY_TAB": "Timeline", "ACTIVITY_TAB_TITLE": "Muestra toda la actividad de este usuario", "PROJECTS_TAB": "Proyectos", "PROJECTS_TAB_TITLE": "Lista todo los proyectos en los que este usuario es miembro", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Proyectos", "HELP": "Reordena tus proyectos para ver arriba los que más utilizas.
Los 10 primeros aparecerán en la lista de proyectos de la barra de navegación de arriba.", "PRIVATE": "Proyecto privado", + "LOOKING_FOR_PEOPLE": "Este proyecto está buscando a gente", + "FANS_COUNTER_TITLE": "{total, plural, one{un fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{un observador} other{# observadores}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{un miembro} other{# miembros}}", "STATS": { "PROJECT": "puntos
proyecto", "DEFINED": "puntos
definidos", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Buscar en...", "ACTION_CREATE_PROJECT": "Crear proyecto", "ACTION_IMPORT_PROJECT": "Importar proyecto", - "SEE_MORE_PROJECTS": "Ver más proyectos", + "MANAGE_PROJECTS": "Administrar proyectos", "TITLE_CREATE_PROJECT": "Crear proyecto", "TITLE_IMPORT_PROJECT": "Importar proyecto", "TITLE_PRVIOUS_PROJECT": "Mostrar proyectos anteriores", "TITLE_NEXT_PROJECT": "Mostrar siguientes proyectos", "HELP_TITLE": "Página de Soporte de Taiga", "HELP": "Ayuda", + "HOMEPAGE": "Página de inicio", "FEEDBACK_TITLE": "Envíanos tu feedback", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Edita la configuración de tus notificaciones", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Escribe un nuevo comentario aquí", "SHOW_DELETED": "Mostrar comentarios eliminados", "HIDE_DELETED": "Ocultar comentarios eliminados", + "DELETE": "Borrar comentario", "RESTORE": "Restaurar comentario" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Admin", "CUSTOMIZE_GRAPH_TITLE": "Modifica los puntos y sprints a través de la zona de Administración", "MOVE_US_TO_CURRENT_SPRINT": "Mover al Sprint en curso", + "MOVE_US_TO_LATEST_SPRINT": "Mover al Sprint más reciente", "SHOW_FILTERS": "Mostrar filtros", "SHOW_TAGS": "Ver etiquetas", "EMPTY": "¡El backlog está vacío!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Ir al panel de tareas de \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", "EMPTY": "Todavía no hay Sprints", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "Este sprint no tiene historias de usuario", "WARNING_EMPTY_SPRINT": "Arrastra aquí las historias desde tu backlog para comenzar un nuevo sprint", "TITLE_ACTION_NEW_SPRINT": "+ Nuevo sprint", "TEXT_ACTION_NEW_SPRINT": "Es posible que desees crear un nuevo sprint para tu proyecto", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "La imagen se escalará a 80x80px
", + "IMAGE_HELP": "La imagen se escalará a 80x80px.", "ACTION_CHANGE_IMAGE": "Cambiar", - "ACTION_USE_GRAVATAR": "Usar imagen de Gravatar", + "ACTION_USE_GRAVATAR": "Usar imagen por defecto", "ACTION_DELETE_ACCOUNT": "Elimina cuenta de Taiga", "CHANGE_EMAIL_SUCCESS": "¡Revisa tu bandeja de entrada!
Hemos enviado un mail a tu cuenta
con instrucciones para establecer tu nueva dirección", "CHANGE_PHOTO": "Cambiar foto", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Los equipos ahora pueden crear atributos personalizados como un medio flexible para introducir datos específicos útiles para su flujo de trabajo particular", "HINT3_TITLE": "Reordenar tus proyectos para facilitar el acceso a los más relevantes para ti", "HINT3_TEXT": "Los 10 primeros proyectos aparecerán en el listado del acceso directo de la barra superior.", - "HINT4_TITLE": "¿Has olvidado en qué estabas trabajando?", + "HINT4_TITLE": "¿Olvidaste lo que estabas trabajando?", "HINT4_TEXT": "No te preocupes, en el dashboard encontrarás las tareas, peticiones e historias de usuario abiertas en el orden en el que trabajste en ellas." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} se ha unido a Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Haciendo click en \"Registrarme\"', aceptas nuestros
términos de servicio y nuestra política de privacidad." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Cuando crea una nueva cuenta, usted está de acuerdo con nuestros
términos de servicio y políticas de privacidad." }, "EXTERNAL_APP": { "PAGE_TITLE": "Una aplicación externa requiere autenticación", @@ -1354,6 +1384,12 @@ "CANCEL": "Cancelar" }, "JOYRIDE": { + "NAV": { + "NEXT": "Siguiente", + "BACK": "Back", + "SKIP": "Saltar paso", + "DONE": "Hecha" + }, "DASHBOARD": { "STEP1": { "TITLE": "Su proyecto", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Observando", - "TEXT1": "Y justo aquí encontrarás aquellas sobre las que quieres saber algo más", + "TEXT1": "Y aquí encontraras aquellas de tus proyectos sobre las que quieres estar informado.", "TEXT2": "Ya estás trabajando con Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Buena suerte!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Descubre proyectos", + "DISCOVER_SUBTITLE": "{projects, plural, one{Un proyecto público por descubrir} other{# proyectos público por descubrir}}\n", + "MOST_ACTIVE": "Más activos", + "MOST_ACTIVE_EMPTY": "No hay proyectos con actividad todavía", + "MOST_LIKED": "Favoritos", + "MOST_LIKED_EMPTY": "No hay proyectos marcados como FAVORITOS todavía.", + "VIEW_MORE": "Ver más", + "RECRUITING": "Este proyecto está buscando a gente", + "FEATURED": "Proyectos Destacados", + "EMPTY": "No hay proyectos que mostrar para estos criterios de búsqueda.
¡Inténtalo de nuevo!", + "FILTERS": { + "ALL": "Todas", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Buscando a gente", + "WEEK": "Última semana", + "MONTH": "Último mes", + "YEAR": "Último año", + "ALL_TIME": "Todos", + "CLEAR": "Borrar filtros" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Escribe algo...", + "ACTION_TITLE": "Buscar", + "RESULTS": "Resultados de búsqueda" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-fi.json b/app/locales/taiga/locale-fi.json index 1a1e05aa..8058e3cb 100644 --- a/app/locales/taiga/locale-fi.json +++ b/app/locales/taiga/locale-fi.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Kyllä", "NO": "Ei", + "OR": "or", "LOADING": "Ladataan...", "LOADING_PROJECT": "Ladataan projektia...", "DATE": "DD.MM.YY", @@ -18,14 +19,16 @@ "TAG_LINE": "Ketterä, ilmainne avoimen koodin projektinhallintaohjelmisto", "TAG_LINE_2": "Rakasta projektiasi", "BLOCK": "Estä", - "UNBLOCK": "Salli", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Suljettu", + "UNBLOCK": "Salli", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Ole hyvä ja anna syy", "CREATED_BY": "Luonut {{fullDisplayName}}", "FROM": "käyttäjältä", "TO": "käyttäjälle", "CLOSE": "sulje", - "BLOCKED_NOTE": "Miksi tämä käyttäjätarina on suljettu?", - "BLOCKED_REASON": "Ole hyvä ja anna syy", "GO_HOME": "Vie minut kotiin", "PLUGINS": "Pluginit", "BETA": "Tämä on beta-versio!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "ulkoinen käyttäjä", "GENERIC_ERROR": "Oompa Loompas havaitsivat virheen {{error}}.", "IOCAINE_TEXT": "Jos tehtävä ahdistaa, merkitse se myrkylliseksi. Ajan mittaa pieninä annoksina saattaa kastokykysi myrkkyä vastaan parantua.", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Be careful! You're writing with capital letters and this input is case sensitive.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Tämä arvo vaikuttaa virheelliseltä.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Tekijää ei valittu", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Poista tekijä", "REMOVE_ASSIGNED": "Poista tekijä", "TOO_MANY": "...liikaa käyttäjiä, lisää suodattimia", "CONFIRM_UNASSIGNED": "Haluatko varmasti jättää tämän ilman tekijää?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Muokkaa tekijää" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Muokkaa tekijää", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Suljettu", "OPEN": "Avoin" }, "WATCHERS": { + "WATCHERS": "Watchers", "ADD": "Add watchers", "TITLE_ADD": "Add a project member to the watchers list", "DELETE": "Poista vahti", @@ -176,7 +184,7 @@ "SAVE": "Tallenna oma kenttä", "EDIT": "Muokkaa omaa kenttää", "DELETE": "Poista oma attribuutti", - "CONFIRM_DELETE": "Muista että oman kentän arvot poistetaan kentän mukana.
Jatketaanko?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "suodattimet", @@ -274,9 +282,9 @@ "HEADER": "Minulla on jo Taiga tunnus", "PLACEHOLDER_AUTH_NAME": "Käyttäjänimi tai sähköposti (kirjainkoko merkitsevä)", "LINK_FORGOT_PASSWORD": "Unohditko?", - "TITLE_LINK_FORGOT_PASSWORD": "Unohditko salasanasi?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?", "ACTION_ENTER": "Sisään", - "ACTION_SIGN_IN": "Kirjaudu sisään", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Salasana (kirjainkoko merkitsevä)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "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": "It feels empty, doesn't it? Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "You don't have any projects yet", "WORKING_ON_SECTION": "Working on", - "WATCHING_SECTION": "Watching" + "WATCHING_SECTION": "Watching", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "My projects - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "liitteet", "TITLE": "{{ fileName }} ladattu {{ date }}\n", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Kirjoita lyhyt kuvaus", "DEPRECATED": "(poistettu)", "DEPRECATED_FILE": "Vanhentunut?", "ADD": "Add new attachment. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Maks. koko: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ näytä vanhentuneet liitteet", "HIDE_DEPRECATED": "- piilota vanhentuneet liitteet", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Poista liite...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "liite '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Emme pystyneet poistamaan: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) on liian iso Oompa Loompaseille, kokeile pienemmällä kuin ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "on vanhentunut" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)", "TAGS": "Avainsanat", "DESCRIPTION": "Kuvaus", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Julkinen projekti", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Yksityinen projekti", - "DELETE": "Poista tämä projekti" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Poista tämä projekti", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Raportit", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "The people with whom you work at Taiga will be your contacts automatically", "REPORT": "Report Abuse", "TABS": { - "ACTIVITY_TAB": "Aktiivisuus", + "ACTIVITY_TAB": "Timeline", "ACTIVITY_TAB_TITLE": "Show all the activity of this user", "PROJECTS_TAB": "Projektit", "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projektit", "HELP": "Reorder your projects to set in the top the most used ones.
The top 10 projects will appear in the top navigation bar project list", "PRIVATE": "Yksityinen projekti", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "projekti
pisteet", "DEFINED": "määritely
pistettä", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Hae täältä...", "ACTION_CREATE_PROJECT": "Luo projekti", "ACTION_IMPORT_PROJECT": "Import project", - "SEE_MORE_PROJECTS": "See more projects", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Create project", "TITLE_IMPORT_PROJECT": "Import project", "TITLE_PRVIOUS_PROJECT": "Näytä aikaisemmat projektit", "TITLE_NEXT_PROJECT": "Näytä seuraavat projektit", "HELP_TITLE": "Taiga Support Page", "HELP": "Help", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Lähetä palautetta", "FEEDBACK": "Palaute", "NOTIFICATIONS_TITLE": "Edit your notification settings", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Lisää uusi kommentti tässä", "SHOW_DELETED": "Näytä poistettu kommentti", "HIDE_DELETED": "Piilota poistettu kommentti", + "DELETE": "Delete comment", "RESTORE": "Palauta kommentti" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Hallinnoi", "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", "MOVE_US_TO_CURRENT_SPRINT": "Siirrä nykyiseen kierrokseen", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Näytä suodattimet", "SHOW_TAGS": "Näytä avainsanat", "EMPTY": "The backlog is empty!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Siirry tehtävätauluun {{name}}", "NUMBER_SPRINTS": "
kierroksia", "EMPTY": "There are no sprints yet", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", "TITLE_ACTION_NEW_SPRINT": "Add new sprint", "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "Kuvan kooksi astetaan 80x80px.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Muuta", - "ACTION_USE_GRAVATAR": "Käytä gravatar kuvaa", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Poista Taiga-tunnus", "CHANGE_EMAIL_SUCCESS": "Tarkista sähköpostisi!
Lähetimme ohjeet
{{email}}
uuden osoitteen asettamiseen", "CHANGE_PHOTO": "Muuta kuva", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Did you forgot what were you working on?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Don't worry, on your dashboard you'll find your open tasks, issues, and user stories in the order you worked on them." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} has joined Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "An external app requires authentication", @@ -1354,6 +1384,12 @@ "CANCEL": "Peru" }, "JOYRIDE": { + "NAV": { + "NEXT": "Seuraava", + "BACK": "Palvelin", + "SKIP": "Skip", + "DONE": "Tehty" + }, "DASHBOARD": { "STEP1": { "TITLE": "Projektisi", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Seuraa", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Good luck!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Kaikki", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Type something...", + "ACTION_TITLE": "Hae", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-fr.json b/app/locales/taiga/locale-fr.json index b1d82c74..a9ebd4cc 100644 --- a/app/locales/taiga/locale-fr.json +++ b/app/locales/taiga/locale-fr.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Oui", "NO": "Non", + "OR": "ou", "LOADING": "Veuillez patienter...", "LOADING_PROJECT": "Chargement du projet...", "DATE": "DD MMM YYYY", @@ -12,38 +13,42 @@ "DELETE": "Supprimer", "CREATE": "Créer", "ADD": "Ajouter", - "COPY_TO_CLIPBOARD": "Copier dans le presse-papier: Ctrl+C", + "COPY_TO_CLIPBOARD": "Copier dans le presse-papier : Ctrl+C", "EDIT": "Éditer", "DRAG": "Faire glisser", "TAG_LINE": "Votre outil de gestion de projet agile, libre et open source", "TAG_LINE_2": "AIMEZ VOTRE PROJET", "BLOCK": "Bloquer", - "UNBLOCK": "Débloquer", + "BLOCK_TITLE": "Bloquer cet élément si, par exemple, il a une dépendance qui ne peut être satisfaite", "BLOCKED": "Bloqué", + "UNBLOCK": "Débloquer", + "UNBLOCK_TITLE": "Débloquer cet élément", + "BLOCKED_NOTE": "Pourquoi est-ce bloqué ?", + "BLOCKED_REASON": "Veillez s'il vous plait indiquer la raison", "CREATED_BY": "Créé par {{fullDisplayName}}", "FROM": "de", "TO": "à", "CLOSE": "clos", - "BLOCKED_NOTE": "Pourquoi ce récit utilisateur est-il bloqué ?", - "BLOCKED_REASON": "Veillez s'il vous plait indiquer la raison", "GO_HOME": "Retour à l'accueil", "PLUGINS": "Plugins", - "BETA": "Version beta!", + "BETA": "Version beta !", "ONE_ITEM_LINE": "Un élément par ligne...", "NEW_BULK": "Nouvel ajout en bloc", "RELATED_TASKS": "Tâches associées", "LOGOUT": "Déconnexion", - "EXTERNAL_USER": "um usuário externo", + "EXTERNAL_USER": "un utilisateur externe", "GENERIC_ERROR": "L'un de nos Oompa Loompas dit {{error}}.", "IOCAINE_TEXT": "Vous vous sentez un peu submergé par une tâche ? Soyez certains d'en informer les autres en cliquant su Iocaine lors de la modification de la tâche. Il est possible de s'immuniser contre ce poison (fictif) en consommant de petites quantités en heures supplémentaires, tout comme il est possible de s'améliorer en acceptant parfois de nouveaux défis !", - "CAPSLOCK_WARNING": "Attention! Vous tapez en lettres capitales et ce champ est sensible à la case.", + "CLIENT_REQUIREMENT": "Un besoin client est un nouveau besoin qui n'était pas prévu et qu'il est nécessaire d'intégrer au projet", + "TEAM_REQUIREMENT": "Un besoin projet est un besoin qui est nécessaire au projet mais qui ne doit avoir aucun impact pour le client", + "CAPSLOCK_WARNING": "Attention ! Vous tapez en lettres majuscules et ce champ est sensible à la casse.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Cette valeur semble être invalide.", "TYPE_EMAIL": "Cette valeur devrait être une adresse courriel valide.", - "TYPE_URL": "O valor deveria ser uma url válida.", - "TYPE_URLSTRICT": "O valor deveria ser uma url válida.", - "TYPE_NUMBER": "O valor deveria ser número válido.", - "TYPE_DIGITS": "O valor deveria conter digitos.", + "TYPE_URL": "Cette valeur doit être une url valide.", + "TYPE_URLSTRICT": "Cette valeur doit être une url valide.", + "TYPE_NUMBER": "Cette valeur doit être un nombre valide.", + "TYPE_DIGITS": "Cette valeur doit être numérique.", "TYPE_DATEISO": "Cette valeur n'est pas une date valide (AAAA-MM-JJ).", "TYPE_ALPHANUM": "Cette valeur devrait être alphanumérique.", "TYPE_PHONE": "Cette valeur devrait être un numéro de téléphone valide.", @@ -64,7 +69,7 @@ }, "PICKERDATE": { "FORMAT": "DD MMM YYYY", - "IS_RTL": "false", + "IS_RTL": "faux", "FIRST_DAY_OF_WEEK": "1", "PREV_MONTH": "Mois précédent", "NEXT_MONTH": "Mois suivant", @@ -125,7 +130,7 @@ "IS_CLOSED": "Est fermé ?", "STATUS": "Statut", "TYPE": "Type", - "SEVERITY": "Sévérité", + "SEVERITY": "Gravité", "PRIORITY": "Priorité", "ASSIGNED_TO": "Affecté à", "POINTS": "Points", @@ -139,29 +144,32 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Non affecté", + "ASSIGN": "Assigné", "DELETE_ASSIGNMENT": "Supprimer l'affectation", "REMOVE_ASSIGNED": "Supprimer l'affectation", "TOO_MANY": "...trop d'utilisateurs ; filtrez davantage", "CONFIRM_UNASSIGNED": "Etes-vous sûr de vouloir continuer sans affectation ?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Modifier l'affectation" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Modifier l'affectation", + "SELF": "Me l'assigné" }, "STATUS": { "CLOSED": "Fermé", "OPEN": "Ouvert" }, "WATCHERS": { - "ADD": "Ajouter un suiveur", - "TITLE_ADD": "Ajoutez un membre du projet à la liste de suiveurs", + "WATCHERS": "Observateurs", + "ADD": "Ajouter des observateurs", + "TITLE_ADD": "Ajoutez un membre du projet à la liste de observateurs", "DELETE": "Supprimer l'observateur", "TITLE_LIGHTBOX_DELETE_WARTCHER": "Supprimer l'observateur..." }, "WATCH_BUTTON": { "WATCH": "Suivre", - "WATCHING": "Observant", + "WATCHING": "Suivi", "UNWATCH": "Ne plus suivre", - "WATCHERS": "Suiveurs", + "WATCHERS": "Observateurs", "BUTTON_TITLE": "Suivre/ne plus suivre cet élément", - "COUNTER_TITLE": "{total, plural, one{un suiveur} other{# suiveurs}}" + "COUNTER_TITLE": "{total, plural, one{un observateur} other{# observateurs}}" }, "VOTE_BUTTON": { "UPVOTE": "Vote positif", @@ -176,7 +184,7 @@ "SAVE": "Sauver le champ personnalisé", "EDIT": "Modifier un champ personnalisé", "DELETE": "Supprimer un attribut personnalisé", - "CONFIRM_DELETE": "Toutes les valeurs dans ce champ personnalisé vont être supprimées.
Etes-vous sure de vouloir continuer ?" + "CONFIRM_DELETE": "Souvenez-vous que toutes les valeurs de ce champ personnalisé vont être effacées.\nEtes-vous sûr de vouloir continuer ?" }, "FILTERS": { "TITLE": "filtres", @@ -261,7 +269,7 @@ } }, "LOGIN": { - "PAGE_TITLE": "Connexion - Taiga", + "PAGE_TITLE": "Se connecter - Taiga", "PAGE_DESCRIPTION": "Connectez-vous à Taiga, une plateforme de gestion de projets pour les startups, les développeurs et les designers agiles qui veulent un outil simple, beau et qui rend le travail vraiment agréable." }, "AUTH": { @@ -273,10 +281,10 @@ "LOGIN_COMMON": { "HEADER": "J'ai déjà un identifiant Taiga", "PLACEHOLDER_AUTH_NAME": "Nom d'utilisateur ou adresse courriel (sensible à la casse)", - "LINK_FORGOT_PASSWORD": "Mot de passe oublié?", + "LINK_FORGOT_PASSWORD": "Mot de passe oublié ?", "TITLE_LINK_FORGOT_PASSWORD": "Avez-vous oublié votre mot de passe ?", "ACTION_ENTER": "Entrer", - "ACTION_SIGN_IN": "Connexion", + "ACTION_SIGN_IN": "Se connecter", "PLACEHOLDER_AUTH_PASSWORD": "Mot de passe (sensible à la casse)" }, "LOGIN_FORM": { @@ -295,27 +303,27 @@ "PLACEHOLDER_PASSWORD": "Saisissez un mot de passe (sensible à la casse)", "ACTION_SIGN_UP": "S'enregistrer", "TITLE_LINK_LOGIN": "Se connecter", - "LINK_LOGIN": "Êtes-vous déjà enregistré? Connectez-vous." + "LINK_LOGIN": "Êtes-vous déjà enregistré ? Connectez-vous." }, "FORGOT_PASSWORD": { "PAGE_TITLE": "Mot de passe oublié - Taiga", "PAGE_DESCRIPTION": "Saisissez votre nom d'utilisateur ou votre adresse courriel pour recevoir un nouveau mot de passe et accéder à nouveau à Taiga." }, "FORGOT_PASSWORD_FORM": { - "TITLE": "Oops, avez-vous oublié votre mot de passe ?", + "TITLE": "Oups, avez-vous oublié votre mot de passe ?", "SUBTITLE": "Saisissez votre nom d'utilisateur ou votre adresse courriel pour en obtenir un nouveau", "PLACEHOLDER_FIELD": "Nom d'utilisateur ou adresse courriel", "ACTION_RESET_PASSWORD": "Réinitialiser le mot de passe", "LINK_CANCEL": "Nan, ramenez-moi en arrière. Je crois que je m'en souviens.", - "SUCCESS": "Consultez votre messagerie!
Nous venons d'envoyer un courriel avec les instructions pour créer un nouveau mot de passe", + "SUCCESS": "Consultez votre messagerie !
Nous venons d'envoyer un courriel avec les instructions pour créer un nouveau mot de passe", "ERROR": "D'après nos Oompa Loompas, vous n'êtes pas encore enregistré." }, "CHANGE_PASSWORD": { "PAGE_TITLE": "Modifier votre mot de passe - Taiga", "PAGE_DESCRIPTION": "Paramètrez un nouveau mot de passe pour votre compte Taiga et, au fait, vous devriez manger de la nourriture plus riche en fer, c'est bon pour votre cerveau :P", "SECTION_NAME": "Modifier le mot de passe", - "FIELD_CURRENT_PASSWORD": "Mot de passe courant", - "PLACEHOLDER_CURRENT_PASSWORD": "Votre mot de passe courant (ne rien saisir si vous ne possédez pas encore de mot de passe)", + "FIELD_CURRENT_PASSWORD": "Mot de passe actuel", + "PLACEHOLDER_CURRENT_PASSWORD": "Votre mot de passe actuel (vide si vous n'avez pas encore de mot de passe)", "FIELD_NEW_PASSWORD": "Nouveau mot de passe", "PLACEHOLDER_NEW_PASSWORD": "Entrez un nouveau mot de passe", "FIELD_RETYPE_PASSWORD": "Confirmez le mot de passe", @@ -343,11 +351,12 @@ "HOME": { "PAGE_TITLE": "Accueil - Taiga", "PAGE_DESCRIPTION": "La page d'accueil de Taiga sur laquelle apparaissent vos projets principaux et toutes les récits utilisateur, tâches et tickets qui vont sont assignés et surveillés.", - "EMPTY_WORKING_ON": "C'est un peu vide, n'est-ce pas? Commencez à utiliser Taiga et vous verrez ici les stories, tâches et issues sur lesquelles vous travaillez.", - "EMPTY_WATCHING": "Suivez les User Stories, Tâches, Issues, ... dont vous voulez être informé :)", + "EMPTY_WORKING_ON": "C'est un peu vide, n'est-ce pas ? Commencez à utiliser Taiga et vous verrez ici les récits, tâches et tickets sur lesquels vous travaillez.", + "EMPTY_WATCHING": "Suivez des récits utilisateur, des tâches, des tickets dans vos projets et soyez prévenu des modifications :)", "EMPTY_PROJECT_LIST": "Vous n'avez aucun projet pour l'instant", "WORKING_ON_SECTION": "Projets en cours", - "WATCHING_SECTION": "Observant" + "WATCHING_SECTION": "Suivi", + "DASHBOARD": "Tableau de bord des projets" }, "PROJECTS": { "PAGE_TITLE": "Mes projets - Taiga", @@ -356,21 +365,25 @@ }, "ATTACHMENT": { "SECTION_NAME": "pièces jointes", - "TITLE": "{{ fileName }} téléversé le {{ date }}", + "TITLE": "{{ fileName }} déposé le {{ date }}", + "LIST_VIEW_MODE": "Vue sous forme de liste", + "GALLERY_VIEW_MODE": "Vue sous forme de galerie", "DESCRIPTION": "Saisissez une courte description", "DEPRECATED": "(obsolète)", - "DEPRECATED_FILE": "Obsolète?", + "DEPRECATED_FILE": "Obsolète ?", "ADD": "Ajoutez une nouvelle pièce jointe. {{maxFileSizeMsg}}", - "MAX_FILE_SIZE": "[Taille max.: {{maxFileSize}}]", + "DROP": "Déposer les pièces jointes ici !", + "MAX_FILE_SIZE": "[Taille max. : {{maxFileSize}}]", "SHOW_DEPRECATED": "+ montrer les pièces jointes obsolètes", "HIDE_DEPRECATED": "- cacher les pièces jointes obsolètes", "COUNT_DEPRECATED": "({{ counter }} obsolètes)", - "MAX_UPLOAD_SIZE": "La taille maximum de téléversement est {{maxFileSize}}", + "MAX_UPLOAD_SIZE": "La taille maximum de dépôt est {{maxFileSize}}", "DATE": "DD MMM YYYY [at] hh:mm", "ERROR_UPLOAD_ATTACHMENT": "Nous n'avons pas été capable de téléverser '{{fileName}}'. {{errorMessage}}", "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Supprimer la pièce jointe...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "la pièce jointe \"{{fileName}}\"", "ERROR_DELETE_ATTACHMENT": "Nous n'avons pas été capable de supprimer {{errorMessage}}.", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) est un peu trop lourd pour nos Oompa Loompas, réessayez avec un fichier d'une taille inférieure à ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "est obsolète" } @@ -387,7 +400,7 @@ "HELP": "Avez-vous besoin d'aide ? Consultez notre page d'assistance !", "PROJECT_DEFAULT_VALUES": { "TITLE": "Valeurs par défaut", - "SUBTITLE": "Définir les valeurs par défaut pour toutes les listes de sélection" + "SUBTITLE": "Définir les valeurs par défaut pour toutes les listes de sélection." }, "MEMBERSHIPS": { "TITLE": "Gérer les membres", @@ -413,7 +426,7 @@ "TITLE": "Modules", "ENABLE": "Activer", "DISABLE": "Désactiver", - "BACKLOG": "Historique", + "BACKLOG": "Backlog", "BACKLOG_DESCRIPTION": "Gérez votre récits utilisateur pour garder une vue organisée des travaux à venir et priorisés.", "KANBAN": "Kanban", "KANBAN_DESCRIPTION": "Organisez votre projet de manière agile avec ce tableau.", @@ -432,17 +445,26 @@ "URL_CHAT_ROOM": "URL du salon de discussion" }, "PROJECT_PROFILE": { - "PAGE_TITLE": "{{sectionName}} - Profil projet - {{projectName}}\n", + "PAGE_TITLE": "{{sectionName}} - Profil projet - {{projectName}}", "PROJECT_DETAILS": "Détails du projet", "PROJECT_NAME": "Nom du projet", "PROJECT_SLUG": "Label du projet", "NUMBER_SPRINTS": "Nombre de sprints (0 pour non déterminé)", - "NUMBER_US_POINTS": "Nombre de points d'US (0 pour non déterminé)", + "NUMBER_US_POINTS": "Nombre de points de RU (0 pour non déterminé)", "TAGS": "Mots-clés", "DESCRIPTION": "Description", + "RECRUITING": "Est-ce que ce projet recherche des membres ?", + "RECRUITING_MESSAGE": "Qui cherchez-vous ?", + "RECRUITING_PLACEHOLDER": "Définissez le profil que vous recherchez", "PUBLIC_PROJECT": "Projet public", + "PUBLIC_PROJECT_DESC": "Les membres vont pouvoir chercher et voir votre projet.", "PRIVATE_PROJECT": "Projet privé", - "DELETE": "Supprimer ce projet" + "PRIVATE_PROJECT_DESC": "Par défaut, ce project va être caché au public", + "PRIVATE_OR_PUBLIC": "Quelle est la différence entre les projets publics et privés ?", + "DELETE": "Supprimer ce projet", + "LOGO_HELP": "L'image va être agrandie à 80x80px", + "CHANGE_LOGO": "Changer le logo", + "ACTION_USE_DEFAULT_LOGO": "Utiliser l'image par défaut " }, "REPORTS": { "TITLE": "Rapports", @@ -450,10 +472,10 @@ "DESCRIPTION": "Téléchargez un fichier CSV ou copier l'URL générée puis ouvrez la dans votre éditeur de texte ou tableur préféré pour créer votre propre rapports de projet. Vous pourrez facilement visualiser et analyser toutes vos données.", "HELP": "Comment utiliser ceci dans ma propre feuille de calcul?", "REGENERATE_TITLE": "Changer l'URL", - "REGENERATE_SUBTITLE": "Vous êtes sur le point de changer l'url d'accès aux données CSV. L'url précédente sera désactivée. Êtes-vous sûre ?" + "REGENERATE_SUBTITLE": "Vous êtes sur le point de changer l'url d'accès aux données CSV. L'url précédente sera désactivée. Êtes-vous sûr ?" }, "CSV": { - "SECTION_TITLE_US": "Rapports des récits utilisateur", + "SECTION_TITLE_US": "rapports des récits utilisateur", "SECTION_TITLE_TASK": "rapports des tâches", "SECTION_TITLE_ISSUE": "Rapports des tickets", "DOWNLOAD": "Télécharger au format CSV", @@ -463,7 +485,7 @@ "ACTION_REGENERATE": "Regénérer" }, "CUSTOM_FIELDS": { - "TITLE": "Champs Personnalisés", + "TITLE": "Champs personnalisés", "SUBTITLE": "Spécifiez les champs personnalisés de vos récits utilisateur, tâches et tickets", "US_DESCRIPTION": "Champs personnalisés des récits utilisateur", "US_ADD": "Ajouter un champ personnalisé dans les récits utilisateur", @@ -493,21 +515,21 @@ "ACTION_ADD": "Ajouter une priorité" }, "PROJECT_VALUES_SEVERITIES": { - "TITLE": "Sévérités", - "SUBTITLE": "Spécifiez les sévérités qu'auront vos tickets", - "ISSUE_TITLE": "Sévérités des tickets", - "ACTION_ADD": "Ajouter un degré de sévérité" + "TITLE": "Gravité", + "SUBTITLE": "Spécifiez la gravité qu'auront vos tickets", + "ISSUE_TITLE": "Gravité des tickets", + "ACTION_ADD": "Ajouter un niveau de gravité" }, "PROJECT_VALUES_STATUS": { "TITLE": "Statut", "SUBTITLE": "Spécifiez les statuts que vont prendre vos récits utilisateur, tâches et tickets", - "US_TITLE": "Statuts des HU", - "TASK_TITLE": "Statuts des Tâches", + "US_TITLE": "Statuts des RU", + "TASK_TITLE": "Statuts des tâches", "ISSUE_TITLE": "Statuts des Tickets" }, "PROJECT_VALUES_TYPES": { "TITLE": "Types", - "SUBTITLE": "Spécifiez les priorités qu'auront vos bugs", + "SUBTITLE": "Spécifiez les types de ticket possibles", "ISSUE_TITLE": "Types de tickets", "ACTION_ADD": "Ajouter un nouveau {{objName}}" }, @@ -517,13 +539,13 @@ "HELP_ROLE_ENABLED": "Si activé, les membres affectés à ce rôle pourront estimer la valeur du point pour les récits utilisateurs", "DISABLE_COMPUTABLE_ALERT_TITLE": "Désactiver les estimations pour ce rôle", "DISABLE_COMPUTABLE_ALERT_SUBTITLE": "Si vous désactivez les estimations pour le rôle {{roleName}} toutes les estimations précédentes faites par ce rôle seront supprimées", - "DISABLE_COMPUTABLE_ALERT_MESSAGE": "Êtes-vous sûr de vouloir désactiver les estimations de ce rôle?", + "DISABLE_COMPUTABLE_ALERT_MESSAGE": "Êtes-vous sûr de vouloir désactiver les estimations de ce rôle ?", "COUNT_MEMBERS": "{{ role.members_count }} membres avec ce rôle", "TITLE_DELETE_ROLE": "Supprimer des rôles", "REPLACEMENT_ROLE": "Tous les utilisateurs avec ce rôle seront déplacés dans", - "WARNING_DELETE_ROLE": "Attention, toutes les estimations de role seront supprimées", + "WARNING_DELETE_ROLE": "Attention, toutes les estimations de rôle seront supprimées", "ERROR_DELETE_ALL": "Vous ne pouvez pas supprimer toutes les valeurs", - "EXTERNAL_USER": " " + "EXTERNAL_USER": "Utilisateur externe " }, "THIRD_PARTIES": { "SECRET_KEY": "Clé secrète", @@ -579,7 +601,7 @@ "CANCEL_TITLE": "Annuler la création", "SET_FIELD_NAME": "Saisissez le nom du champ personnalisé", "SET_FIELD_DESCRIPTION": "Entrez la description de votre champ personnalisé", - "FIELD_TYPE_DEFAULT": "-- sélectionnez un --", + "FIELD_TYPE_DEFAULT": "-- sélectionner --", "ACTION_UPDATE": "Mettre à jour le champ personnalisé", "ACTION_CANCEL_EDITION": "Annuler l'édition" }, @@ -603,7 +625,7 @@ "LABEL_US": "Valeur par défaut pour la sélection du récit utilisateur", "LABEL_TASK_STATUS": "Valeur par défaut pour la sélection de l'état des tâches", "LABEL_PRIORITY": "Valeur par défaut de la sélection des priorités", - "LABEL_SEVERITY": "Valeur par défaut pour le sélecteur de sévérité", + "LABEL_SEVERITY": "Valeur par défaut pour le sélecteur de gravité", "LABEL_ISSUE_TYPE": "Valeur par défaut pour le sélecteur de type", "LABEL_ISSUE_STATUS": "Valeur par défaut pour le sélecteur de statut de bug" }, @@ -615,7 +637,7 @@ }, "US_STATUS": { "ACTION_ADD_STATUS": "Ajouter un nouveau status", - "IS_ARCHIVED_COLUMN": "Est archivé?", + "IS_ARCHIVED_COLUMN": "Est archivé ?", "WIP_LIMIT_COLUMN": "Limite de Work In Progress", "PLACEHOLDER_WRITE_NAME": "Donnez un nom au nouvel état" }, @@ -635,7 +657,7 @@ "STATUS": "Etats", "POINTS": "Points", "PRIORITIES": "Priorités", - "SEVERITIES": "Sévérités", + "SEVERITIES": "Gravité", "TYPES": "Types", "CUSTOM_FIELDS": "Champs personnalisés" }, @@ -665,12 +687,12 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Les personnes avec qui vous travaillez à Taiga seront vos contacts automatiquement", "REPORT": "Signaler un abus", "TABS": { - "ACTIVITY_TAB": "Activité", + "ACTIVITY_TAB": "Chronologie", "ACTIVITY_TAB_TITLE": "Voir toutes les activités de cet utilisateur", "PROJECTS_TAB": "Projets", - "PROJECTS_TAB_TITLE": "Liste de tous les projets pour lesquels l'utilisateur est membre.", - "LIKES_TAB": "Aime", - "LIKES_TAB_TITLE": "Lister tous les 'aime' fait par cet utilisateur", + "PROJECTS_TAB_TITLE": "Liste de tous les projets dont l'utilisateur est membre", + "LIKES_TAB": "J'aime", + "LIKES_TAB_TITLE": "Lister tous les \"J'aime\" de cet utilisateur", "VOTES_TAB": "Votes", "VOTES_TAB_TITLE": "Lister tous les votes faits par cet utilisateur", "WATCHED_TAB": "Suivre", @@ -690,7 +712,7 @@ "FILTER_TYPE_ALL_TITLE": "Voir tous", "FILTER_TYPE_PROJECTS": "Projets", "FILTER_TYPE_PROJECT_TITLES": "Voir uniquement les projets", - "FILTER_TYPE_USER_STORIES": "Stories", + "FILTER_TYPE_USER_STORIES": "Récits", "FILTER_TYPE_USER_STORIES_TITLES": "Voir uniquement les user stories", "FILTER_TYPE_TASKS": "Tâches", "FILTER_TYPE_TASK_TITLES": "Voir uniquement les tâches", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projets", "HELP": "Réorganisez vos projets pour placer les plus utilisés en premier.
Les 10 premiers projets apparaitront dans la liste des projets de la barre de navigation supérieure", "PRIVATE": "Projet privé", + "LOOKING_FOR_PEOPLE": "Est-ce que ce projet recherche des membres ?", + "FANS_COUNTER_TITLE": "{total, plural, one{un fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{un observateur} other{# observateurs}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{un membre} other{# membres}}", "STATS": { "PROJECT": "points
projet", "DEFINED": "points
définis", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Rechercher dans...", "ACTION_CREATE_PROJECT": "Créer un projet", "ACTION_IMPORT_PROJECT": "Importer un projet", - "SEE_MORE_PROJECTS": "Voir plus de projets", + "MANAGE_PROJECTS": "Gérer les projets", "TITLE_CREATE_PROJECT": "Créer un projet", "TITLE_IMPORT_PROJECT": "Importer un projet", "TITLE_PRVIOUS_PROJECT": "Montrer les projets précédents", "TITLE_NEXT_PROJECT": "Montrer les projets suivants", "HELP_TITLE": "Page d'assistance Taiga", "HELP": "Aide", + "HOMEPAGE": "Page d'accueil ", "FEEDBACK_TITLE": "Envoyez vos commentaires", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Modifier vos réglages de notification", @@ -755,11 +782,11 @@ }, "IMPORT": { "TITLE": "Importation du projet en cours", - "UPLOADING_FILE": "Téléversement du fichier de vidage", + "UPLOADING_FILE": "Dépôt du fichier de vidage", "DESCRIPTION": "Ce processus peut prendre du temps, veuillez garder cette fenêtre ouverte.", "ASYNC_IN_PROGRESS_TITLE": "Nos Oompa Loompas sont en train d'importer votre projet", "ASYNC_IN_PROGRESS_MESSAGE": "Ce processus pourrait durer plusieurs minutes
Nous vous enverrons un courriel quand ce sera prêt", - "UPLOAD_IN_PROGRESS_MESSAGE": "{{uploadedSize}} de {{totalSize}} uploadés", + "UPLOAD_IN_PROGRESS_MESSAGE": "{{uploadedSize}} de {{totalSize}} déposés", "ERROR": "Nos Oompas Loompas ont rencontré des problèmes en important vos données de vidage. Merci de réessayer.", "ERROR_TOO_MANY_REQUEST": "Nous sommes désolés, nos Oompa Loompas sont très occupés en ce moment. Veuillez réessayer dans quelques minutes.", "ERROR_MESSAGE": "Nos Oompa Loompas ont des problèmes pour importer vos données de vidage : {{error_message}}", @@ -767,8 +794,8 @@ "SYNC_SUCCESS": "Votre projet a été importé avec succès" }, "LIKE_BUTTON": { - "LIKE": "Aimer", - "LIKED": "Aimé", + "LIKE": "J'aime", + "LIKED": "J'aime", "UNLIKE": "Je n'aime plus", "BUTTON_TITLE": "Aimer ou ne plus aimer ce projet", "COUNTER_TITLE": "{total, plural, one{un fan} other{# fans}}" @@ -776,8 +803,8 @@ "WATCH_BUTTON": { "BUTTON_TITLE": "Suivre ce projet et définir une politique de notification", "WATCH": "Suivre", - "WATCHING": "Observant", - "COUNTER_TITLE": "{total, plural, one{un suiveur} other{# suiveurs}}", + "WATCHING": "Suivi", + "COUNTER_TITLE": "{total, plural, one{un observateur} other{# observateurs}}", "OPTIONS": { "NOTIFY_ALL": "Recevoir toutes les notifications", "NOTIFY_ALL_TITLE": "Recevoir toutes les notifications pour ce projet", @@ -792,13 +819,13 @@ "DELETE_ACCOUNT": { "SECTION_NAME": "Supprimer le compte Taiga", "CONFIRM": "Etes-vous sûr de vouloir supprimer votre compte Taiga ?", - "SUBTITLE": "Vous allez nous manquer :(", + "SUBTITLE": "Vous allez nous manquer ! :-(", "NEWSLETTER_LABEL_TEXT": "Je ne veux plus recevoir votre bulletin d'information" }, "DELETE_PROJECT": { "TITLE": "Supprimer le projet", - "QUESTION": "Êtes-vous sûr de vouloir supprimer ce projet?", - "SUBTITLE": "Toutes les données ( récit utilisateur, tâches, tickets, sprints et pages de wiki) seront perdues! :-(", + "QUESTION": "Êtes-vous sûr de vouloir supprimer ce projet ?", + "SUBTITLE": "Toutes les données du projet (récits utilisateur, tâches, tickets, sprints et pages de wiki) seront perdues ! :-(", "CONFIRM": "Oui, je suis sûr" }, "ASSIGNED_TO": { @@ -813,7 +840,7 @@ "TITLE": "Ajouter un ticket" }, "FEEDBACK": { - "TITLE": "Dites nous quelque chose...", + "TITLE": "Dites-nous quelque chose...", "COMMENT": "...un bug, des suggestions, quelque chose de cool... ou même votre pire cauchemar avec Taiga", "ACTION_SEND": "Envoyez vos commentaires" }, @@ -827,7 +854,7 @@ "PLACEHOLDER_SPRINT_START": "Début estimé", "PLACEHOLDER_SPRINT_END": "Fin estimée", "ACTION_DELETE_SPRINT": "Voulez-vous supprimer ce sprint ?", - "TITLE_ACTION_DELETE_SPRINT": "supprimer un sprint", + "TITLE_ACTION_DELETE_SPRINT": "supprimer le sprint", "LAST_SPRINT_NAME": "le dernier sprint est {{lastSprint}} ;-) " }, "CREATE_EDIT_TASK": { @@ -858,35 +885,36 @@ "SECTION_NAME": "Détails du récit utilisateur", "LINK_TASKBOARD": "Tableau des tâches", "TITLE_LINK_TASKBOARD": "Aller au tableau des tâches", - "TOTAL_POINTS": "Total des points", - "ADD": "Ajouter un nouveau récit utilisateur", + "TOTAL_POINTS": "total des points", + "ADD": "+ Créer un récit utilisateur", "ADD_BULK": "Ajouter de nouveaux récits utilisateur en lot", "PROMOTED": "Ce récit utilisateur a été promue à partir d'un ticket :", "TITLE_LINK_GO_TO_ISSUE": "Aller vers ce ticket", "EXTERNAL_REFERENCE": "Ce récit utilisateur a été créé depuis", "GO_TO_EXTERNAL_REFERENCE": "Allez à l'origine", "BLOCKED": "Ce récit utilisateur est bloqué", - "PREVIOUS": "user story précédente", + "PREVIOUS": "récit utilisateur précédent", "NEXT": "récit utilisateur suivant", - "TITLE_DELETE_ACTION": "Supprimer ce récit utilisateur", - "LIGHTBOX_TITLE_BLOKING_US": "US bloquante", + "TITLE_DELETE_ACTION": "Supprimer le récit utilisateur", + "LIGHTBOX_TITLE_BLOKING_US": "Bloque le RU", "TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tâches complétées", - "ASSIGN": "Affecter ce récit utilisateur", + "ASSIGN": "Affecter le récit utilisateur", "NOT_ESTIMATED": "Non estimé", - "TOTAL_US_POINTS": "Total des points HU", + "TOTAL_US_POINTS": "Total des points RU", "FIELDS": { - "TEAM_REQUIREMENT": "Exigence de l'équipe", - "CLIENT_REQUIREMENT": "Exigence du client", + "TEAM_REQUIREMENT": "Besoin projet", + "CLIENT_REQUIREMENT": "Besoin client", "FINISH_DATE": "Date de fin" } }, "COMMENTS": { - "DELETED_INFO": "Commentaire supprimé par {{user}} le {{date}}\n", + "DELETED_INFO": "Commentaire supprimé par {{user}} le {{date}}", "TITLE": "Commentaires", "COMMENT": "Commentaire", "TYPE_NEW_COMMENT": "Entrez un nouveau commentaire ici", "SHOW_DELETED": "Afficher le commentaire supprimé", "HIDE_DELETED": "Cacher le commentaire supprimé", + "DELETE": "Supprimer le commentaire", "RESTORE": "Restaurer le commentaire" }, "ACTIVITY": { @@ -912,13 +940,13 @@ "SUBJECT": "objet", "NAME": "nom", "DESCRIPTION": "description", - "CONTENT": "conteúdo", + "CONTENT": "contenu", "STATUS": "état", - "IS_CLOSED": "está fechado", + "IS_CLOSED": "est fermé", "FINISH_DATE": "date de fin", "TYPE": "type", "PRIORITY": "priorité", - "SEVERITY": "sévérité", + "SEVERITY": "gravité", "ASSIGNED_TO": "affecté à", "WATCHERS": "observateurs", "MILESTONE": "sprint", @@ -927,56 +955,57 @@ "IS_BLOCKED": "est bloqué", "BLOCKED_NOTE": "note bloquée", "POINTS": "points", - "CLIENT_REQUIREMENT": "exigence du client", - "TEAM_REQUIREMENT": "exigence de l'équipe", + "CLIENT_REQUIREMENT": "besoin client", + "TEAM_REQUIREMENT": "besoin projet", "IS_IOCAINE": "est sous iocaine", "TAGS": "mots-clés", "ATTACHMENTS": "pièces jointes", "IS_DEPRECATED": "est obsolète", "ORDER": "classement", - "BACKLOG_ORDER": "Classement du carnet", - "SPRINT_ORDER": "Classement du sprint", + "BACKLOG_ORDER": "classement du backlog", + "SPRINT_ORDER": "classement du sprint", "KANBAN_ORDER": "Classement du Kanban", "TASKBOARD_ORDER": "trier le tableau des tâches", - "US_ORDER": "Classement des récits utilisateur" + "US_ORDER": "classement des récits utilisateur" } }, "BACKLOG": { - "PAGE_TITLE": "Carnet - {{projectName}}", - "PAGE_DESCRIPTION": "Le panneau carnet avec les récits utilisateurs et sprints du projet {{projectName}} : {{projectDescription}}", - "SECTION_NAME": "Carnet", + "PAGE_TITLE": "Backlog - {{projectName}}", + "PAGE_DESCRIPTION": "Le panneau backlog avec les récits utilisateurs et sprints du projet {{projectName}} : {{projectDescription}}", + "SECTION_NAME": "Backlog", "CUSTOMIZE_GRAPH": "Personnalisez votre backlog", "CUSTOMIZE_GRAPH_TEXT": "Pour obtenir un graphique agréable qui vous aide à suivre l'évolution du projet vous devez définir les points et les sprints via", "CUSTOMIZE_GRAPH_ADMIN": "Admin", "CUSTOMIZE_GRAPH_TITLE": "Définissez les points et les sprints via l'admin", - "MOVE_US_TO_CURRENT_SPRINT": "Déplacer vers le Sprint Courant", + "MOVE_US_TO_CURRENT_SPRINT": "Déplacer vers le sprint actuel", + "MOVE_US_TO_LATEST_SPRINT": "Déplacer vers le dernier sprint", "SHOW_FILTERS": "Afficher les filtres", "SHOW_TAGS": "Afficher les mots-clés", - "EMPTY": "Le carnet de suivi est vide !", + "EMPTY": "Le backlog est vide !", "CREATE_NEW_US": "Créer un nouveau récit utilisateur", - "CREATE_NEW_US_EMPTY_HELP": "Vous pouvez envisager de créer un nouveau récit utilisateur", + "CREATE_NEW_US_EMPTY_HELP": "Vous pouvez envisager de créer un récit utilisateur", "EXCESS_OF_POINTS": "Excès de points", "PENDING_POINTS": "Points restants", "CLOSED_POINTS": "fermé", "COMPACT_SPRINT": "Sprint Compact", "GO_TO_TASKBOARD": "Aller au tableau des tâches de {{::name}}", - "EDIT_SPRINT": "Éditer le Sprint", + "EDIT_SPRINT": "Modifier le Sprint", "TOTAL_POINTS": "total", "STATUS_NAME": "Nom de l'état", - "SORTABLE_FILTER_ERROR": "Vous ne pouvez pas glisser-déposer quand les filtres sont ouverts", + "SORTABLE_FILTER_ERROR": "Vous ne pouvez pas déposer sur le backlog quand les filtres sont ouverts", "DOOMLINE": "Périmètre projet", "CHART": { "XAXIS_LABEL": "Sprints", "YAXIS_LABEL": "Points", "OPTIMAL": "Le nombre optimal de points pour le sprint \"{{sprintName}}\" devrait être {{value}}", "REAL": "Le nombre réel de points pour le sprint \"{{sprintName}}\" est {{value}}", - "INCREMENT_TEAM": "Le nombre de points ajoutés par les exigences de l'équipe pour le sprint \"{{sprintName}}\" est {{value}}", - "INCREMENT_CLIENT": "Le nombre de points ajoutés par les exigences du client pour le sprint \"{{sprintName}}\" est {{value}}" + "INCREMENT_TEAM": "Le nombre de points ajoutés par les besoins projet pour le sprint \"{{sprintName}}\" est {{value}}", + "INCREMENT_CLIENT": "Le nombre de points ajoutés par les besoins client pour le sprint \"{{sprintName}}\" est {{value}}" }, "TAGS": { - "TOGGLE": "Afficher/Cacher les tags", - "SHOW": "Montrer les étiquettes", - "HIDE": "Cacher les étiquettes" + "TOGGLE": "Afficher/Cacher les mots-clés", + "SHOW": "Montrer les mots-clés", + "HIDE": "Cacher les mots-clés" }, "TABLE": { "COLUMN_US": "Récits utilisateur", @@ -1004,16 +1033,17 @@ "HIDE": "Cacher les filtres", "SHOW": "Afficher les filtres", "FILTER_CATEGORY_STATUS": "Etat", - "FILTER_CATEGORY_TAGS": "Labels" + "FILTER_CATEGORY_TAGS": "Mots-clés" }, "SPRINTS": { "TITLE": "SPRINTS", "DATE": "DD MMM YYYY", - "LINK_TASKBOARD": "Taskboard du sprint", - "TITLE_LINK_TASKBOARD": "Aller au Taskboard de \"{{name}}\"", + "LINK_TASKBOARD": "Tableau des tâches", + "TITLE_LINK_TASKBOARD": "Aller au tableau des tâches de \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", - "EMPTY": "There are no sprints yet", - "WARNING_EMPTY_SPRINT": "Déposez ici des Stories de votre backlog pour démarrer un nouveau sprint", + "EMPTY": "Il n'y a pas encore de sprint", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "Ce sprint n'a pas de \"User Stories\"", + "WARNING_EMPTY_SPRINT": "Déposez ici les récits depuis votre backlog pour démarrer un nouveau sprint", "TITLE_ACTION_NEW_SPRINT": "Ajouter un sprint", "TEXT_ACTION_NEW_SPRINT": "Vous souhaitez peut être créer un nouveau sprint dans votre projet.", "ACTION_SHOW_CLOSED_SPRINTS": "Afficher les sprints fermés", @@ -1033,11 +1063,11 @@ "PAGE_DESCRIPTION": "Sprint {{sprintName}} (du {{startDate}} au {{endDate}}) de {{projectName}}. Achevé {{completedPercentage}}% ({{completedPoints}} sur {{totalPoints}} points). {{openTasks}} tâches ouvertes sur {{totalTasks}}.", "SECTION_NAME": "Tableau des tâches", "TITLE_ACTION_ADD": "Ajouter une nouvelle tâche", - "TITLE_ACTION_ADD_BULK": "Ajouter de nouvelles tâches en lot", + "TITLE_ACTION_ADD_BULK": "Ajouter un nouveau lot de tâches", "TITLE_ACTION_ASSIGN": "Affecter une tâche", "TITLE_ACTION_EDIT": "Modifier la tâche", "PLACEHOLDER_CARD_TITLE": "Cela pourrait être une tâche", - "PLACEHOLDER_CARD_TEXT": "Eclater les Stories en tâches pour les suivre séparemment", + "PLACEHOLDER_CARD_TEXT": "Éclater les récits en tâches pour les suivre séparément", "TABLE": { "COLUMN": "Récit utilisateur", "TITLE_ACTION_FOLD": "Replier la colonne", @@ -1048,7 +1078,7 @@ "ROW_UNASSIGED_TASKS_TITLE": "Tâches non assignées" }, "CHARTS": { - "XAXIS_LABEL": "Journées", + "XAXIS_LABEL": "Jours", "YAXIS_LABEL": "Points", "OPTIMAL": "Le nombre optimal de points pour le jour {{formattedDate}} devrait être {{roundedValue}}", "REAL": "Le nombre réel de points pour le jour {{formattedDate}} est {{roundedValue}}", @@ -1057,7 +1087,7 @@ }, "TASK": { "PAGE_TITLE": "{{taskSubject}} - Tâche {{taskRef}} - {{projectName}}", - "PAGE_DESCRIPTION": "État: {{taskStatus }}. Description: {{taskDescription}}", + "PAGE_DESCRIPTION": "État : {{taskStatus }}. Description : {{taskDescription}}", "SECTION_NAME": "Détails de la tâche", "LINK_TASKBOARD": "Tableau des tâches", "TITLE_LINK_TASKBOARD": "Aller au tableau des tâches", @@ -1078,11 +1108,11 @@ "IS_IOCAINE": "est sous iocaine" }, "ACTION_IOCAINE": "locaine", - "TITLE_ACTION_IOCAINE": "Vous vous sentez un peu dépassé par une tâche? Assurez-vous que les autres le savent en cliquant sur locaine en éditant une tâche. Il est possible de s'immuniser contre ce poison (fictif) en en consommant de faibles doses pendant un moment tout comme il est possible de devenir meilleur à ce que vous faîtes en relevant quelques défis à l'occasion!" + "TITLE_ACTION_IOCAINE": "Vous vous sentez un peu dépassé par une tâche ? Assurez-vous que les autres le savent en cliquant sur locaine en éditant une tâche. Il est possible de s'immuniser contre ce poison (fictif) en en consommant de faibles doses pendant un moment tout comme il est possible de devenir meilleur à ce que vous faîtes en relevant quelques défis à l'occasion !" }, "NOTIFICATION": { "OK": "Tout est ok", - "WARNING": "Oh là là, quelque chose s'est produit...", + "WARNING": "Oups, quelque chose s'est produit...", "WARNING_TEXT": "Nos Oompa Loompas sont tristes, vos modifications n'ont pas été sauvegardées !", "SAVED": "Nos Oompa Loompas ont sauvegardé toutes vos modifications !", "CLOSE": "Fermer la notification", @@ -1093,7 +1123,7 @@ "TITLE": "Annuler votre compte", "SUBTITLE": "Nous sommes tristes de vous voir quitter la taïga, et nous espérons que vous avez apprécié votre séjour. :)", "PLACEHOLDER_INPUT_TOKEN": "Annuler le jeton du compte", - "ACTION_LEAVING": "Oui, je m'en vais!", + "ACTION_LEAVING": "Oui, je m'en vais !", "SUCCESS": "Nos Oompa Loompas ont supprimé votre compte" }, "CHANGE_EMAIL_FORM": { @@ -1109,23 +1139,23 @@ "LIST_SECTION_NAME": "Tickets", "SECTION_NAME": "Détails du ticket", "ACTION_NEW_ISSUE": "+ NOUVEAU TICKET", - "ACTION_PROMOTE_TO_US": "Transformer en User Story", + "ACTION_PROMOTE_TO_US": "Promouvoir en récit utilisateur", "PLACEHOLDER_FILTER_NAME": "Écrivez le nom du filtre et appuyez sur \"Entrée\"", "PROMOTED": "Le ticket a été promu en récit utilisateur", "EXTERNAL_REFERENCE": "Ce ticket a été créé à partir de", "GO_TO_EXTERNAL_REFERENCE": "Aller à l'origine", "BLOCKED": "Ce bug est bloqué", - "TITLE_PREVIOUS_ISSUE": "bug précédent", + "TITLE_PREVIOUS_ISSUE": "ticket précédent", "TITLE_NEXT_ISSUE": "ticket suivant", "ACTION_DELETE": "Supprimer le ticket", "LIGHTBOX_TITLE_BLOKING_ISSUE": "Ticket bloquant", "FIELDS": { "PRIORITY": "Priorité", - "SEVERITY": "Sévérité", + "SEVERITY": "Gravité", "TYPE": "Type" }, "CONFIRM_PROMOTE": { - "TITLE": "Transformer ce ticket en un nouveau récit utilisateur", + "TITLE": "Promouvoir ce ticket en nouveau récit utilisateur", "MESSAGE": "Êtes-vous sure de vouloir créer un nouveau récit utilisateur à partir de ce ticket ?" }, "FILTERS": { @@ -1138,7 +1168,7 @@ "CATEGORIES": { "TYPE": "Type", "STATUS": "Statut", - "SEVERITY": "Sévérité", + "SEVERITY": "Gravité", "PRIORITIES": "Priorités", "TAGS": "Mots-clés", "ASSIGNED_TO": "Affecté à", @@ -1153,7 +1183,7 @@ "TABLE": { "COLUMNS": { "TYPE": "Type", - "SEVERITY": "Sévérité", + "SEVERITY": "Gravité", "PRIORITY": "Priorité", "SUBJECT": "Objet", "VOTES": "Votes", @@ -1165,14 +1195,14 @@ "TITLE_ACTION_ASSIGNED_TO": "Affecté à", "BLOCKED": "Bloqué", "EMPTY": { - "TITLE": "Aucun ticket rapporté :-)", + "TITLE": "Il n'y a aucun ticket déclaré :-)", "SUBTITLE": "Avez-vous trouvé un problème ?" } } }, "ISSUE": { "PAGE_TITLE": "{{issueSubject}} - Ticket {{issueRef}} - {{projectName}}", - "PAGE_DESCRIPTION": "État : {{issueStatus }}. Type : {{issueType}}, Priorité : {{issuePriority}}. Sévérité : {{issueSeverity}}. Description : {{issueDescription}}" + "PAGE_DESCRIPTION": "État : {{issueStatus }}. Type : {{issueType}}, Priorité : {{issuePriority}}. Gravité : {{issueSeverity}}. Description : {{issueDescription}}" }, "KANBAN": { "PAGE_TITLE": "Kanban - {{projectName}}", @@ -1186,11 +1216,11 @@ "TITLE_ACTION_ADD_BULK": "Ajout en masse", "ACTION_SHOW_ARCHIVED": "Montrer les éléments archivés", "ACTION_HIDE_ARCHIVED": "Cacher les éléments archivés", - "HIDDEN_USER_STORIES": "Les user stories avec ce statut sont masquées par défaut", + "HIDDEN_USER_STORIES": "Les récits utilisateur avec ce statut sont masqués par défaut", "ARCHIVED": "Vous avez archivé", - "UNDO_ARCHIVED": "Refaites le glisser-déposer pour annuler", - "PLACEHOLDER_CARD_TITLE": "Ce sont vos User Stories", - "PLACEHOLDER_CARD_TEXT": "Les stories peuvent avoir des sous-tâches pour séparer les pré-requis" + "UNDO_ARCHIVED": "Refaites glisser pour annuler", + "PLACEHOLDER_CARD_TITLE": "Ce sont vos récits utilisateurs", + "PLACEHOLDER_CARD_TEXT": "Les récits peuvent avoir des sous-tâches pour séparer les besoins" }, "SEARCH": { "PAGE_TITLE": "Chercher - {{projectName}}", @@ -1201,23 +1231,23 @@ "FILTER_WIKI": "Pages Wiki", "PLACEHOLDER_SEARCH": "Rechercher dans...", "TITLE_ACTION_SEARCH": "rechercher", - "EMPTY_TITLE": "Il semble que vos critères de recherche n'aient rapporté aucun résultat.", + "EMPTY_TITLE": "Il semble qu'aucun résultat ne correspond à vos critères de recherche.", "EMPTY_DESCRIPTION": "Vous pouvez essayer l'un des onglets au-dessus ou relancer une recherche" }, "TEAM": { - "PAGE_TITLE": "EQUIPE - {{projectName}}", + "PAGE_TITLE": "ÉQUIPE - {{projectName}}", "PAGE_DESCRIPTION": "Le panneau équipe montre la liste des membres du projet {{projectName}} : {{projectDescription}}", - "SECTION_NAME": "Equipe", - "APP_TITLE": "EQUIPE - {{projectName}}", + "SECTION_NAME": "Équipe", + "APP_TITLE": "ÉQUIPE - {{projectName}}", "PLACEHOLDER_INPUT_SEARCH": "Rechercher par nom...", - "COLUMN_MR_WOLF": "Mr. Wolf", - "EXPLANATION_COLUMN_MR_WOLF": "Bugs fermés", + "COLUMN_MR_WOLF": "M. Wolf", + "EXPLANATION_COLUMN_MR_WOLF": "Tickets fermés", "COLUMN_IOCAINE": "Buveur de iocaine", "EXPLANATION_COLUMN_IOCAINE": "Doses de iocaine ingérées", "COLUMN_CERVANTES": "Cervantes", "EXPLANATION_COLUMN_CERVANTES": "Page Wiki éditée", "COLUMN_BUG_HUNTER": "Chasseur de bug", - "EXPLANATION_COLUMN_BUG_HUNTER": "Tickets rapportés", + "EXPLANATION_COLUMN_BUG_HUNTER": "Tickets déclarés", "COLUMN_NIGHT_SHIFT": "Equipe de nuit", "EXPLANATION_COLUMN_NIGHT_SHIFT": "Tâches fermées", "COLUMN_TOTAL_POWER": "Puissance totale", @@ -1228,7 +1258,7 @@ "ACTION_LEAVE_PROJECT": "Quitter le projet" }, "USER_SETTINGS": { - "AVATAR_MAX_SIZE": "[Taille max.: {{maxFileSize}}]", + "AVATAR_MAX_SIZE": "[Taille max. : {{maxFileSize}}]", "MENU": { "SECTION_TITLE": "Paramètres de l'utilisateur", "USER_PROFILE": "Profil utilisateur", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "L'image va être mise à l'échelle à 80x80px.
", + "IMAGE_HELP": "L'image va être agrandie à 80x80px", "ACTION_CHANGE_IMAGE": "Modifier", - "ACTION_USE_GRAVATAR": "Utiliser une image gravatar", + "ACTION_USE_GRAVATAR": "Utiliser l'image par défaut ", "ACTION_DELETE_ACCOUNT": "Supprime le compte Taiga", "CHANGE_EMAIL_SUCCESS": "Consultez votre messagerie !
Nous vous avons envoyé un courriel
avec les instructions à suivre pour saisir votre nouvelle adresse", "CHANGE_PHOTO": "Changer la photo", @@ -1266,7 +1296,7 @@ "FULL_NAME": "Nom complet", "PLACEHOLDER_FULL_NAME": "Saisissez votre nom complet (ex. Íñigo Montoya)", "BIO": "Biographie (maximum 210 caractères)", - "PLACEHOLDER_BIO": "Dites en nous plus sur vous", + "PLACEHOLDER_BIO": "Dites-en nous plus sur vous", "LANGUAGE": "Langue", "LANGUAGE_DEFAULT": "-- utiliser la langue par défaut --", "THEME": "Thème", @@ -1283,7 +1313,7 @@ }, "WIKI": { "PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}", - "PAGE_DESCRIPTION": "Dernière modification le {{lastModifiedDate}} ({{totalEditions}} modifications en tout) Contenu: {{ wikiPageContent }}", + "PAGE_DESCRIPTION": "Dernière modification le {{lastModifiedDate}} ({{totalEditions}} modifications en tout) Contenu : {{ wikiPageContent }}", "DATETIME": "DD MMM YYYY HH:mm", "PLACEHOLDER_PAGE": "Ecrivez votre page wiki", "REMOVE": "Supprimer cette page wiki", @@ -1302,17 +1332,17 @@ "SECTION_NAME": "Indice", "LINK": "Si vous voulez savoir comment l'utiliser, visitez notre page d'assistance", "LINK_TITLE": "Visitez notre page d'assistance", - "HINT1_TITLE": "Savez-vous que vous pouvez importer et exporter les projets?", + "HINT1_TITLE": "Savez-vous que vous pouvez importer et exporter les projets ?", "HINT1_TEXT": "Cela vous permet d'extraire toutes vos données d'un Taiga et de le déplacer vers un autre", "HINT2_TITLE": "Savez-vous que vous pouvez créer des champs personnalisés ?", "HINT2_TEXT": "Les équipes peuvent désormais créer des champs personnalisés comme un moyen flexible d'entrer des données spécifiques utiles pour leur flux de travail particulier.", - "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", - "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Avez-vous oublié ce sur quoi vous travailliez ?", + "HINT3_TITLE": "Triez vos projets pour mettre en valeur vos projets les plus pertinents.", + "HINT3_TEXT": "Les 10 projets sont listés dans la barre d'accès direct en haut de la page.", + "HINT4_TITLE": "Vous avez oublié ce sur quoi vous travaillez ?", "HINT4_TEXT": "Pas d'inquiétude, vous trouverez dans votre tableau de bord vos tâches, tickets et récits utilisateurs dans l'ordre dans lequel vous avez travaillé dessus." }, "TIMELINE": { - "UPLOAD_ATTACHMENT": "{{username}} a téléversé une nouvelle pièce jointe dans {{obj_name}}", + "UPLOAD_ATTACHMENT": "{{username}} a déposé une nouvelle pièce jointe dans {{obj_name}}", "US_CREATED": "{{username}} a créé un nouveau récit utilisateur {{obj_name}} dans {{project_name}}", "ISSUE_CREATED": "{{username}} a créé un nouveau ticket {{obj_name}} dans {{project_name}}", "TASK_CREATED": "{{username}} a créé une nouvelle Tâche {{obj_name}} dans {{project_name}}", @@ -1336,36 +1366,42 @@ "NEW_COMMENT_TASK": "{{username}} a commenté la tâche {{obj_name}}", "NEW_MEMBER": "{{project_name}} a un nouveau membre", "US_ADDED_MILESTONE": "{{username}} a ajouté le récit utilisateur {{obj_name}} à {{sprint_name}}", - "US_MOVED": "{{username}} a déplacé l'US {{obj_name}}", + "US_MOVED": "{{username}} a déplacé le RU {{obj_name}}", "US_REMOVED_FROM_MILESTONE": "{{username}} a ajouté le récit utilisateur {{obj_name}} au carnet", "BLOCKED": "{{username}} a bloqué {{obj_name}}", "UNBLOCKED": "{{username}} a débloqué {{obj_name}}", "NEW_USER": "{{username}} a rejoint Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "En cliquant sur \"S'enregistrer\"', vous acceptez nos
conditions de service et notre politique de sécurité." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Lors de la création d'un compte, vous acceptez nos
conditions d'utilisation et politique de confidentialité." }, "EXTERNAL_APP": { "PAGE_TITLE": "Une application externe requiert l'authentification", "PAGE_DESCRIPTION": "Une application externe requiert l'authentification", - "AUTHORIZATION_REQUEST": "Autoriser {{application}} à utiliser votre compte Taiga?", + "AUTHORIZATION_REQUEST": "Autoriser {{application}} à utiliser votre compte Taiga ?", "LOGIN_WITH_ANOTHER_USER": "Se connecter avec un autre utilisateur", "AUTHORIZE_APP": "Autoriser l'application", "CANCEL": "Annuler" }, "JOYRIDE": { + "NAV": { + "NEXT": "Suivant", + "BACK": "Back", + "SKIP": "Passer", + "DONE": "Fait" + }, "DASHBOARD": { "STEP1": { "TITLE": "Votre projet", - "TEXT": "Bienvenue! Ici vous trouverez tous les projets dont vous êtes membre." + "TEXT": "Bienvenue ! Vous trouverez ici tous les projets dont vous êtes membre." }, "STEP2": { "TITLE": "Projets en cours", - "TEXT": "Ici vous trouverez les User Stories, tâches et tickets sur lesquels vous travaillez." + "TEXT": "Ici vous trouverez les récits utilisateur, tâches et tickets sur lesquels vous travaillez." }, "STEP3": { - "TITLE": "Observant", - "TEXT1": "Et ici vous trouverez celles dont vous voulez être informé.", + "TITLE": "Suivi", + "TEXT1": "Et ici vous trouverez ceux que vous voulez connaitre dans vos projets.", "TEXT2": "Vous travaillez déjà avec Taiga ;)" }, "STEP4": { @@ -1382,7 +1418,7 @@ }, "STEP2": { "TITLE": "Backlog du produit", - "TEXT": "Le backlog est la liste des pré-requis (User Stories) pour le projet. Ici vous préparerez vos sprints." + "TEXT": "Le backlog est la liste des besoins (récits utilisateur) pour le projet. C'est ici que vous préparerez vos sprints." }, "STEP3": { "TITLE": "Sprints", @@ -1390,7 +1426,7 @@ }, "STEP4": { "TITLE": "Récits utilisateur", - "TEXT": "Ce sont les pré-requis de haut niveau. Vous pouvez les ajouter au backlog et les déplacer dans le sprint pour lequel elle devront être livrées." + "TEXT": "Ce sont les besoins de niveau élevé. Vous pouvez les ajouter au backlog et les glisser dans le sprint pour lequel elle devront être livrées." } }, "KANBAN": { @@ -1399,14 +1435,42 @@ "TEXT": "Configurez les colonnes de statut dont vous aurez besoin pour lier votre workflow à l'administration." }, "STEP2": { - "TITLE": "User Stories et Tâches", - "TEXT": "Les user stories sont les pré-requis de haut niveau. Vous pouvez les déplacer dans différentes colonnes." + "TITLE": "Récits utilisateur & Tâches", + "TEXT": "Les récits utilisateur sont les besoins de niveau élevé. Vous pouvez les déplacer dans différentes colonnes." }, "STEP3": { - "TITLE": "Ajout d'User Stories", - "TEXT1": "Vous pouvez vouloir ajouter une User Story (icône ajouter une US) ou un groupe d'User Stories (icône provisoir)", - "TEXT2": "Bonne chance!" + "TITLE": "Ajout de récits utilisateur", + "TEXT1": "Vous pouvez ajouter un seul récit utilisateur (icône ajouter un récit utilisateur) ou plusieurs (icône vrac)", + "TEXT2": "Bonne chance !" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Découvrir les projets", + "DISCOVER_SUBTITLE": "{projects, plural, one{Un projet public à découvrir} other{# projets publics à découvrir}}", + "MOST_ACTIVE": "les plus actifs", + "MOST_ACTIVE_EMPTY": "Il n'y a pas de projets actifs pour le moment", + "MOST_LIKED": "Les plus « aimés »", + "MOST_LIKED_EMPTY": "Il n'y a pas encore de projet « aimé »", + "VIEW_MORE": "Voir plus", + "RECRUITING": "Ce projet recherche des membres", + "FEATURED": "Projets mis en avant", + "EMPTY": "Aucun projet ne correspond à ce critère de recherche.
Essayez encore !", + "FILTERS": { + "ALL": "Toutes", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "En recherche de membres", + "WEEK": "Semaine dernière", + "MONTH": "Mois dernier", + "YEAR": "Leanne dernière", + "ALL_TIME": "Tout le temps", + "CLEAR": "Effacer les filtres" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Écrivez quelque chose...", + "ACTION_TITLE": "Rechercher", + "RESULTS": "Résultats de la recherche" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-it.json b/app/locales/taiga/locale-it.json index a69c905d..f4d13625 100644 --- a/app/locales/taiga/locale-it.json +++ b/app/locales/taiga/locale-it.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Sì", "NO": "No", + "OR": "or", "LOADING": "Caricamento...", "LOADING_PROJECT": "Stiamo caricando il progetto...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Il tuo agile, libero e opensource strumento di project management", "TAG_LINE_2": "ADORO IL VOSTRO PROGETTO", "BLOCK": "Blocca", - "UNBLOCK": "Sblocca", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Bloccato", + "UNBLOCK": "Sblocca", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Spiega il motivo", "CREATED_BY": "Creato da {{fullDisplayName}}", "FROM": "da", "TO": "a", "CLOSE": "chiudi", - "BLOCKED_NOTE": "Perchè questa storia è bloccata?", - "BLOCKED_REASON": "Spiega il motivo", "GO_HOME": "Ritorna all'inizio", "PLUGINS": "Plugin", "BETA": "Siamo in beta!", @@ -34,8 +37,10 @@ "RELATED_TASKS": "Compiti correlati", "LOGOUT": "Esci", "EXTERNAL_USER": "un utente esterno", - "GENERIC_ERROR": "C'é uno dei nostri Digital Champions che dice {{error}}.", + "GENERIC_ERROR": "C'é uno dei nostri Oompa Loompa che dice {{error}}.", "IOCAINE_TEXT": "Sei stremato? Assicurati che gli altri lo sappiano cliccando su 'aspirina' quando stai lavorando su un compito che ti affatica. Ma non demordere, ricordati: si può migliorare solo se di tanto in tanto si accettano sfide extra!", + "CLIENT_REQUIREMENT": "Requisito del Cliente è un nuovo requisito che non era stato previsto ed ora è stato richiesto come requisito del progetto", + "TEAM_REQUIREMENT": "Requisito del Team è un requisito che deve esistere nel progetto ma non deve avere costi per il cliente", "CAPSLOCK_WARNING": "Attento! stai scrivendo in maiuscolo e questo input è sensibile alle maiuscole..", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Questo valore non è valido.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Non assegnato", + "ASSIGN": "Assegna", "DELETE_ASSIGNMENT": "Elimina incarico", "REMOVE_ASSIGNED": "Rimuovi incaricato", "TOO_MANY": "...troppi utenti, continua a filtrare", "CONFIRM_UNASSIGNED": "Sei sicuro di volerlo lasciare non assegnato?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Modifica incarico" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Modifica incarico", + "SELF": "Assegna a me" }, "STATUS": { "CLOSED": "Chiuso", "OPEN": "Apri" }, "WATCHERS": { + "WATCHERS": "Osservatori", "ADD": "Aggiungi osservatori", "TITLE_ADD": "Aggiungi un membro di progetto alla lista degli osservatori", "DELETE": "Elimina osservatore", @@ -176,7 +184,7 @@ "SAVE": "Salva il campo personalizzato", "EDIT": "Modifica Campo Personalizzato", "DELETE": "Elimina l'attributo personalizzato", - "CONFIRM_DELETE": "Ricordati che tutti i valori nel campo personalizzato saranno cancellati. Sei sicuro di voler continuare?" + "CONFIRM_DELETE": "Ricorda che tutti i valori in questo campo custom andranno persi.\nSei sicuro di voler continuare?" }, "FILTERS": { "TITLE": "filtri", @@ -274,14 +282,14 @@ "HEADER": "Ho già un account Taiga", "PLACEHOLDER_AUTH_NAME": "nome utente o email (occhio alle maiuscole)", "LINK_FORGOT_PASSWORD": "Password dimenticata?", - "TITLE_LINK_FORGOT_PASSWORD": "Password dimenticata?", + "TITLE_LINK_FORGOT_PASSWORD": "Hai dimenticato la tua password?", "ACTION_ENTER": "Conferma", - "ACTION_SIGN_IN": "Accedi", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Password (occhio alle maiuscole)" }, "LOGIN_FORM": { - "ERROR_AUTH_INCORRECT": "Secondo i nostri Digital Champions, i tuoi nomi utente, email o password sono errati.", - "SUCCESS": "I nostri Digital Champions sono entusiasti! Benvenuto in Taiga!" + "ERROR_AUTH_INCORRECT": "Secondo i nostri Oompa Loompa, i tuoi nomi utente, email o password sono errati.", + "SUCCESS": "I nostri Oompa Loompa sono entusiasti! Benvenuto in Taiga!" }, "REGISTER": { "PAGE_TITLE": "Registrazione - Taiga", @@ -308,7 +316,7 @@ "ACTION_RESET_PASSWORD": "Reimposta la password", "LINK_CANCEL": "Fa niente dai, riportami indietro. Penso di ricordarmela.", "SUCCESS": "Controlla la tua posta!
Ti abbiamo inviato una email con le istruzioni per reimpostare la password", - "ERROR": "Secondo i nostri Digital Champions, non sei ancora registrato." + "ERROR": "Secondo i nostri Oompa Loompa, non sei ancora registrato." }, "CHANGE_PASSWORD": { "PAGE_TITLE": "Cambia la tua password - Taiga", @@ -328,26 +336,27 @@ "PLACEHOLDER_NEW_PASSWORD": "Nuova password", "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Digita una nuova password", "ACTION_RESET_PASSWORD": "Reimposta la password", - "ERROR": "I nostri Digital Champions non riescono a trovare la tua richiesta di recupero password. Prova a chiedergliela di nuovo.", - "SUCCESS": "I nostri Digital Champions hanno salvato la tua nuova password. Che bravi!
Pova a fare login con questa." + "ERROR": "I nostri Oompa Loompa non riescono a trovare la tua richiesta di recupero password. Prova a chiedergliela di nuovo.", + "SUCCESS": "I nostri Oompa Loompa hanno salvato la tua nuova password. Che bravi!
Pova a fare login con questa." }, "INVITATION": { "PAGE_TITLE": "Invito ad accedere - Taiga", "PAGE_DESCRIPTION": "Accetta l'invito a partecipare ad un progetto in Taiga, una piattaforma semplice di project management che rende il lavoro davvero piacevole a start-up e a chi sviluppa e progetta con il metodo \"agile\"." }, "INVITATION_LOGIN_FORM": { - "NOT_FOUND": "I nostri Digital Champions non hanno trovato il tuo invito.", + "NOT_FOUND": "I nostri Oompa Loompa non hanno trovato il tuo invito.", "SUCCESS": "Sei ora un membro di questo progetto. Benvenuto in {{project_name}}", - "ERROR": "Secondo i nostri Digital Champions, non sei ancora regisrtrato o hai inserito una password non valida." + "ERROR": "Secondo i nostri Oompa Loompa, non sei ancora registrato o hai inserito una password non valida." }, "HOME": { "PAGE_TITLE": "Home - Taiga", "PAGE_DESCRIPTION": "La home di Taiga con i tuoi principali progetti e tutte le storie utente, i compiti e problemi assegnati e osservati. ", "EMPTY_WORKING_ON": "Dà un po' l'idea di vuoto, no? Inizia a lavorare con Taiga e qui vedrai le storie, i compiti ed i problemi su cui stai lavorando.", - "EMPTY_WATCHING": "Segui le storie utente, i compiti e i problemi... sui quali vuoi rimanere informato :)", + "EMPTY_WATCHING": "Ossserva Storie Utente, Compiti, Criticità nel progetto e ti verranno notificate le sue modifiche :)", "EMPTY_PROJECT_LIST": "Per ora non hai nessun progetto", "WORKING_ON_SECTION": "Sta lavorando su", - "WATCHING_SECTION": "Osservando" + "WATCHING_SECTION": "Osservando", + "DASHBOARD": "Dashboard Progetti" }, "PROJECTS": { "PAGE_TITLE": "I miei progetti - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "allegati", "TITLE": "{{ fileName }} caricato il {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Inserisci una breve descrizione", "DEPRECATED": "(deprecato)", "DEPRECATED_FILE": "Deprecato?", "ADD": "Aggiungi un nuovo allegato. {{maxFileSizeMsg}}", + "DROP": "Rilascia qui l'allegato!", "MAX_FILE_SIZE": "[Dimensione max. {{maxFileSize}}]", "SHOW_DEPRECATED": "+ visualizza allegati deprecati", "HIDE_DEPRECATED": "- nascondi allegati deprecati", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Cancella l'allegato", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "l'allegato '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Non siamo riusciti a cancellare: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) è troppo pesante per i nostri Oompa Loompa; falli contenti, prova con una dimensione minore di ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "è deprecato" } @@ -405,9 +418,9 @@ "LOADING_MESSAGE": "Non chiudere questa pagina.", "ASYNC_MESSAGE": "Ti invieremo una mail quando sarà pronto", "SYNC_MESSAGE": "Se il download non parte automaticamente clicca here.", - "ERROR": "I nostri Digital Champion hanno qualche problema a generare il tuo dump. Prova di nuova.", - "ERROR_BUSY": "Scusali, i nostri Digital Champions sono occupati. Riprova di nuovo in qualche minuto.", - "ERROR_MESSAGE": "Accidenti, i nostri Digital Champions hanno qualche problema a generare il tuo dump: {{message}}" + "ERROR": "I nostri Oompa Loompa hanno qualche problema a generare il tuo dump. Prova di nuova.", + "ERROR_BUSY": "Scusali, i nostri Oompa Loompa sono occupati. Riprova di nuovo in qualche minuto.", + "ERROR_MESSAGE": "Accidenti, i nostri Oompa Loompa hanno qualche problema a generare il tuo dump: {{message}}" }, "MODULES": { "TITLE": "Moduli", @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Numero di punti delle storie utente (0 come quantità indeterminata)", "TAGS": "Tag", "DESCRIPTION": "Descrizione", + "RECRUITING": "Il progetto cerca persone?", + "RECRUITING_MESSAGE": "Chi stai cercando?", + "RECRUITING_PLACEHOLDER": "Definisci il profilo che stai cercando", "PUBLIC_PROJECT": "Progetto pubblico", + "PUBLIC_PROJECT_DESC": "Gli utenti possono cercare e vedere il tuo progetto", "PRIVATE_PROJECT": "Progetto privato", - "DELETE": "Elimina questo progetto" + "PRIVATE_PROJECT_DESC": "Di default, il progetto sarà nascosto al pubblico", + "PRIVATE_OR_PUBLIC": "Quale è la differenza tra progetto privato e pubblico?", + "DELETE": "Elimina questo progetto", + "LOGO_HELP": "L'immagine sarà scalata a 80x80px.", + "CHANGE_LOGO": "Cambia Logo", + "ACTION_USE_DEFAULT_LOGO": "Usa l'immagine di default" }, "REPORTS": { "TITLE": "Rapporti", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Le persone con cui lavori in Taiga saranno automaticamente tra tuoi contatti", "REPORT": "Segnala abuso", "TABS": { - "ACTIVITY_TAB": "Attività", + "ACTIVITY_TAB": "Cronologia", "ACTIVITY_TAB_TITLE": "Mostra tutte le attività dell'utente", "PROJECTS_TAB": "Progetti", "PROJECTS_TAB_TITLE": "La lista di tutti i progetti di cui l'utente è membro", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Progetti", "HELP": "Riordina i tuoi progetti per avere in alto i più usati.
I primi 10 progetti compariranno nella lista dei progetti della barra di navigazione, su in alto", "PRIVATE": "Progetto privato", + "LOOKING_FOR_PEOPLE": "Il progetto cerca persone", + "FANS_COUNTER_TITLE": "{total, plural, one{un follower} other{# followers}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{un osservatore} other{# osservatori}} ", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{un membro} other{# membri}}", "STATS": { "PROJECT": "progetto
punti", "DEFINED": "Definiti
punti", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Cerca in...", "ACTION_CREATE_PROJECT": "Crea progetto", "ACTION_IMPORT_PROJECT": "Importa progetto", - "SEE_MORE_PROJECTS": "Guarda più progetti", + "MANAGE_PROJECTS": "Gestisci Progetti", "TITLE_CREATE_PROJECT": "Crea progetto", "TITLE_IMPORT_PROJECT": "Importa progetto", "TITLE_PRVIOUS_PROJECT": "Mostra i progetti precedenti", "TITLE_NEXT_PROJECT": "Progetto successivo", "HELP_TITLE": "Pagina di supporto Taiga", "HELP": "Aiuto", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Invia feedback", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Modifica le impostazioni delle notifiche", @@ -757,13 +784,13 @@ "TITLE": "Importazione Progetto", "UPLOADING_FILE": "Carico il file di dump", "DESCRIPTION": "Questo processo puó durare un po', nel frattempo lasciare questa finestra aperta.", - "ASYNC_IN_PROGRESS_TITLE": "I nostri Digital Champions stanno lavorando per importare il tuo progetto!", + "ASYNC_IN_PROGRESS_TITLE": "I nostri Oompa Loompa stanno lavorando per importare il tuo progetto!", "ASYNC_IN_PROGRESS_MESSAGE": "Questo processo puó durare minuti.
Verrá inviata una mail al suo completamento", "UPLOAD_IN_PROGRESS_MESSAGE": "Caricati {{uploadedSize}} di {{totalSize}}", - "ERROR": "Mannaggia, i Digital Champions hanno qualche problema ad importare il dump. Prova di nuovo.", - "ERROR_TOO_MANY_REQUEST": "Scusaci, i nostri Digital Champions sono di nuovo occupati. Riprova di nuovo in qualche minuto.", - "ERROR_MESSAGE": "I nostri Digital Champions hanno qualche problema ad importare il dump dei tuoi dati: {{error_message}}", - "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) è troppo pesante per i nostri Digital Champions; falli contenti, prova con una dimensione minore di ({{maxFileSize}})", + "ERROR": "Mannaggia, i Oompa Loompa hanno qualche problema ad importare il dump. Prova di nuovo.", + "ERROR_TOO_MANY_REQUEST": "Scusaci, i nostri Oompa Loompa sono di nuovo occupati. Riprova di nuovo in qualche minuto.", + "ERROR_MESSAGE": "I nostri Oompa Loompa hanno qualche problema ad importare il dump dei tuoi dati: {{error_message}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) è troppo pesante per i nostri Oompa Loompa; falli contenti, prova con una dimensione minore di ({{maxFileSize}})", "SYNC_SUCCESS": "Il tuo progetto è stato importato con successo" }, "LIKE_BUTTON": { @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Scrivi un nuovo commento qui", "SHOW_DELETED": "Visualizza commento cancellato", "HIDE_DELETED": "Nascondi commento cancellato", + "DELETE": "Delete comment", "RESTORE": "Ripristina commento" }, "ACTIVITY": { @@ -950,9 +978,10 @@ "CUSTOMIZE_GRAPH_ADMIN": "Amministratore", "CUSTOMIZE_GRAPH_TITLE": "Imposta i punti e gli sprint come Amministratore", "MOVE_US_TO_CURRENT_SPRINT": "Spostati allo sprint attuale", + "MOVE_US_TO_LATEST_SPRINT": "Vai allo Sprint più recente", "SHOW_FILTERS": "Mostra filtri", "SHOW_TAGS": "Mostra tag", - "EMPTY": "OMG..Il backlog é pieno!!", + "EMPTY": "OMG..Il backlog é vuoto!!", "CREATE_NEW_US": "Crea una nuova Storia Utente", "CREATE_NEW_US_EMPTY_HELP": "Potresti voler creare una nuova storia utente", "EXCESS_OF_POINTS": "Eccesso di punti", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Vai al pannello dei compiti di \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", "EMPTY": "Non ci sono ancora sprints disponibili", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "Lo Sprint non ha Storie Utente", "WARNING_EMPTY_SPRINT": "Metti qui le storie del tuo backlog che iniziare in nuovo sprint", "TITLE_ACTION_NEW_SPRINT": "Aggiungi un nuovo sprint", "TEXT_ACTION_NEW_SPRINT": "Potresti voler creare un nuovo sprint nel tuo progetto", @@ -1021,12 +1051,12 @@ } }, "ERROR": { - "TEXT1": "E' successo qualcosa, ma i nostri Digital Champions ci stanno lavorando sodo!", + "TEXT1": "E' successo qualcosa, ma i nostri Oompa Loompa ci stanno lavorando sodo!", "NOT_FOUND": "Non trovato", "NOT_FOUND_TEXT": "Errore 404. La pagina che stai cercando non esiste più. Forse puoi tornare alla home e vedere se è possibile trovare quello che stai cercando.", "PERMISSION_DENIED": "Permesso negato", "PERMISSION_DENIED_TEXT": "Non si hanno i permessi necessari per accedere a questa pagina.", - "VERSION_ERROR": "Qualcuno all'interno di Taiga ha fatto un cambiamento prima che i nostri Digital Champion potessero applicare le modifiche. Per favore ricarica e applica di nuovo le modifiche (altrimenti andranno perse)." + "VERSION_ERROR": "Qualcuno all'interno di Taiga ha fatto un cambiamento prima che i nostri Oompa Loompa potessero applicare le modifiche. Per favore ricarica e applica di nuovo le modifiche (altrimenti andranno perse)." }, "TASKBOARD": { "PAGE_TITLE": "{{sprintName}} - Pannello dei compiti dello Sprint - {{projectName}}", @@ -1083,8 +1113,8 @@ "NOTIFICATION": { "OK": "E' tutto ok!", "WARNING": "Oops, é successo qualcosa...", - "WARNING_TEXT": "Accidenti! I nostri Digital Champions sono tristi..le modifiche non sono state salvate", - "SAVED": "I nostri Digital Champions hanno salvato tutte le modifiche! wow!!", + "WARNING_TEXT": "Accidenti! I nostri Oompa Loompa sono tristi..le modifiche non sono state salvate", + "SAVED": "I nostri Oompa Loompa hanno salvato tutte le modifiche! wow!!", "CLOSE": "Chiudi notifica", "MAIL": "Notifica tramite mail", "ASK_DELETE": "Sei sicuro di voler cancellare?" @@ -1094,14 +1124,14 @@ "SUBTITLE": "Ci spiace se stai lasciando Taiga, speriamo che il tuo soggiorno sia stato piacevole :)", "PLACEHOLDER_INPUT_TOKEN": "cancella il token dell'account", "ACTION_LEAVING": "Si, sto abbandonando!", - "SUCCESS": "I nostri Digital Champions hanno eliminato il tuo account, e ora sono tristi" + "SUCCESS": "I nostri Oompa Loompa hanno eliminato il tuo account, e ora sono tristi" }, "CHANGE_EMAIL_FORM": { "TITLE": "Cambia la tua email", "SUBTITLE": "Un'altro click e la tua mail verrá aggiornata!", "PLACEHOLDER_INPUT_TOKEN": "Modifica il token della mail", "ACTION_CHANGE_EMAIL": "Cambia email", - "SUCCESS": "I nostri Digital Champions hanno aggiornato la tua mail" + "SUCCESS": "I nostri Oompa Loompa hanno aggiornato la tua mail" }, "ISSUES": { "PAGE_TITLE": "Criticitá - {{projectName}}", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "L'immagine verrà scalata a 80x80px.
", + "IMAGE_HELP": "L'immagine sarà scalata a 80x80px.", "ACTION_CHANGE_IMAGE": "Cambia", - "ACTION_USE_GRAVATAR": "Usa gravatar", + "ACTION_USE_GRAVATAR": "Usa l'immagine di default", "ACTION_DELETE_ACCOUNT": "Elimina l'account Taiga", "CHANGE_EMAIL_SUCCESS": "Controlla la tua casella di posta
abbiamo mandato una mail al tuo account
con le istruzioni per impostare un nuovo indirizzo", "CHANGE_PHOTO": "Cambia foto", @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} si é iscritto su Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Cliccando \"Inscriviti\"', esprimi il tuo accordo con i nostri
termini di servizi e con la nostra privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Quando crei un nuovo account accetti i nostri
termini del servizio e Politica sulla Privacy." }, "EXTERNAL_APP": { "PAGE_TITLE": "Un'applicazione esterna richiede l'autenticazione", @@ -1354,6 +1384,12 @@ "CANCEL": "Annulla" }, "JOYRIDE": { + "NAV": { + "NEXT": "Successivo", + "BACK": "Back", + "SKIP": "Salta", + "DONE": "Fatto" + }, "DASHBOARD": { "STEP1": { "TITLE": "Il tuo progetto", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "In osservazione", - "TEXT1": "E qui puoi trovare quelle che vuoi tenere d'occhio.", + "TEXT1": "E qui trovi tutte quelle cose del progetto di cui volevi sapere.", "TEXT2": "Stai giù lavorando in Taiga ;) " }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Buona fortuna! " } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Scopri progetti", + "DISCOVER_SUBTITLE": "{projects, plural, one{Un progetto pubblico da scoprire} other{# progetti pubblici da scoprire}}", + "MOST_ACTIVE": "Più attivi", + "MOST_ACTIVE_EMPTY": "Non ci sono ancora progetti ATTIVI", + "MOST_LIKED": "Preferiti", + "MOST_LIKED_EMPTY": "Non ci sono ancora progetti PREFERITI", + "VIEW_MORE": "Vedi altro", + "RECRUITING": "Il progetto cerca persone", + "FEATURED": "Progetti in Vetrina", + "EMPTY": "Non ci sono progetti da mostrare con questi criteri.
Prova ancora!", + "FILTERS": { + "ALL": "Tutti", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Cerca persone", + "WEEK": "Ultima settimana", + "MONTH": "Ultimo mese", + "YEAR": "Ultimo anno", + "ALL_TIME": "Tutto", + "CLEAR": "Cancella filtri" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Inserisci testo...", + "ACTION_TITLE": "Cerca", + "RESULTS": "Risultati della ricerca" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-nl.json b/app/locales/taiga/locale-nl.json index ca0176f6..b111c148 100644 --- a/app/locales/taiga/locale-nl.json +++ b/app/locales/taiga/locale-nl.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Ja", "NO": "Nee", + "OR": "or", "LOADING": "Aan het laden...", "LOADING_PROJECT": "Project laden...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Jouw agile, gratis en open source project management tool", "TAG_LINE_2": "HOU VAN JE PROJECT", "BLOCK": "Blokkeer", - "UNBLOCK": "Deblokkeer", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Geblokkeerd", + "UNBLOCK": "Deblokkeer", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Gelieve de reden uit te leggen", "CREATED_BY": "Aangemaakt door {{fullDisplayName}}", "FROM": "van", "TO": "aan", "CLOSE": "sluiten", - "BLOCKED_NOTE": "Waarom is deze user story geblokkeerd?", - "BLOCKED_REASON": "Gelieve de reden uit te leggen", "GO_HOME": "Neem me naar de homepage", "PLUGINS": "Plugins", "BETA": "We zijn in beta!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "een extern gebruiker", "GENERIC_ERROR": "Een van onze Oempa Loempa's zegt {{error}}.", "IOCAINE_TEXT": "Voel je je wat overweldigd door een taak? Zorg ervoor dat anderen dit weten door op Iocaine te klikken bij het wijzigen van een taak. Je kan stapsgewijs immuun worden tegen dit (fictioneel) dodelijk gif door kleine dosissen op te nemen. Net zoals je beter kan worden in wat je doet door af en toe een extra uitdaging aan te gaan!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Let op! Je schrijft met alleen hoofdletters en dit veld is hoofdlettergevoelig.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Deze waarde lijkt ongeldig te zijn", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Niet toegewezen", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Toewijzing verwijderen", "REMOVE_ASSIGNED": "Verwijder toegewezene", "TOO_MANY": "...te veel gebruikers, blijf filteren", "CONFIRM_UNASSIGNED": "Weet je zeker dat je deze niet wilt toewijzen?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Toewijzing bewerken" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Toewijzing bewerken", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Gesloten", "OPEN": "Open" }, "WATCHERS": { + "WATCHERS": "Watchers", "ADD": "Add watchers", "TITLE_ADD": "Add a project member to the watchers list", "DELETE": "Verwijder waarnemer", @@ -176,7 +184,7 @@ "SAVE": "Eigen velden opslaan", "EDIT": "Eigen velden bewerken", "DELETE": "Verwijder eigen attribuut", - "CONFIRM_DELETE": "Hou er rekening mee dat alle waarden in dit aangepast veld verwijderd zullen worden.
Ben je zeker dat je ermee door wil gaan?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "filters", @@ -274,9 +282,9 @@ "HEADER": "Ik heb al een Taiga login", "PLACEHOLDER_AUTH_NAME": "Gebruikersnaam of email (case sensitive)", "LINK_FORGOT_PASSWORD": "Vergeten?", - "TITLE_LINK_FORGOT_PASSWORD": "Ben je jouw wachtwoord vergeten?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?", "ACTION_ENTER": "Enter", - "ACTION_SIGN_IN": "Log in", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Wachtwoord (hoofdlettergevoelig)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Home -Taiga", "PAGE_DESCRIPTION": "De Taiga start pagina met jouw projecten en de aan jou toegewezen en gevolgde user stories, taken en issues", "EMPTY_WORKING_ON": "It feels empty, doesn't it? Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "Je hebt nog geen projecten", "WORKING_ON_SECTION": "Werkt aan", - "WATCHING_SECTION": "Volgers" + "WATCHING_SECTION": "Volgers", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "Mijn projecten - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "bijlagen", "TITLE": "{{ fileName }} toegevoegd op {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Geef een korte beschrijving", "DEPRECATED": "(verouderd)", "DEPRECATED_FILE": "Verouderd?", "ADD": "Nieuwe bijlage toevoegen. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Max. grootte: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ toon verouderde bijlagen", "HIDE_DEPRECATED": "- verberg verouderde bijlagen", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Verwijder bijlage", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "de bijlage '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "We zijn er niet in geslaagd om te verwijderen: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) is te zwaar voor onze Oempa-Loempa's, probeer het met een bestand kleiner dan ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "is verouderd" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Aantal US punten (0 voor een onbepaalde hoeveelheid)", "TAGS": "Tags", "DESCRIPTION": "Beschrijving", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Open project", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Gesloten project", - "DELETE": "Verwijder dit project" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Verwijder dit project", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Rapporten", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "De mensen waarmee je via Taiga werkt zullen automatisch je contacten worden", "REPORT": "Meld misbruik", "TABS": { - "ACTIVITY_TAB": "Activiteit", + "ACTIVITY_TAB": "Tijdlijn", "ACTIVITY_TAB_TITLE": "Alle activiteit van deze gebruiker weergeven", "PROJECTS_TAB": "Projecten", "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projecten", "HELP": "Herorden je projecten met de vaakst gebruikte bovenaan.
De top 10 projecten zullen verschijnen in de project lijst bovenaan in de navigatie bar.", "PRIVATE": "Gesloten project", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "project
punten", "DEFINED": "gedefinieerde
punten", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Zoeken in...", "ACTION_CREATE_PROJECT": "Project aanmaken", "ACTION_IMPORT_PROJECT": "Importeer project", - "SEE_MORE_PROJECTS": "Bekijk meer projecten", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Project aanmaken", "TITLE_IMPORT_PROJECT": "Importeer project", "TITLE_PRVIOUS_PROJECT": "Toon voorgaande projecten", "TITLE_NEXT_PROJECT": "Toon volgende projecten", "HELP_TITLE": "Taiga support pagina", "HELP": "Help", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Stuur feedback", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Notificatie instellingen bewerken", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Type hier nieuw commentaar", "SHOW_DELETED": "Toon verwijderd commentaar", "HIDE_DELETED": "Verberg verwijderde opmerkingen", + "DELETE": "Delete comment", "RESTORE": "Opmerking herstellen" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Admin", "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", "MOVE_US_TO_CURRENT_SPRINT": "Verplaats naar huidige sprint", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Toon filters", "SHOW_TAGS": "Toon tags", "EMPTY": "The backlog is empty!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Ga naar taakbord van \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", "EMPTY": "There are no sprints yet", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", "TITLE_ACTION_NEW_SPRINT": "Add new sprint", "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "Deze afbeelding zal geschaalt worden naar 80x80px
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Verander", - "ACTION_USE_GRAVATAR": "Gebruik gravatar afbeelding", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Verwijderd Taiga account", "CHANGE_EMAIL_SUCCESS": "Controleer je inbox!
We hebben je een email gestuurd met instructie om je nieuwe adres in te stellen", "CHANGE_PHOTO": "Foto wijzigen", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Ben je vergeten waar je aan hebt gewerkt?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Maak je geen zorgen, op je dashboard vind je al jouw open taken, issues en user stories in de volgorde waarin je hebt gewerkt." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} heeft zich aangemeld voor Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "An external app requires authentication", @@ -1354,6 +1384,12 @@ "CANCEL": "Annuleren" }, "JOYRIDE": { + "NAV": { + "NEXT": "Volgende", + "BACK": "Back", + "SKIP": "Skip", + "DONE": "Afgewerkt" + }, "DASHBOARD": { "STEP1": { "TITLE": "Your project", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Volgers", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Good luck!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Alles", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Typ iets...", + "ACTION_TITLE": "Zoek", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-pl.json b/app/locales/taiga/locale-pl.json index f5e280a5..4cf96bd1 100644 --- a/app/locales/taiga/locale-pl.json +++ b/app/locales/taiga/locale-pl.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Tak", "NO": "Nie", + "OR": "or", "LOADING": "Wczytywanie...", "LOADING_PROJECT": "Wczytywanie projektu...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Twoje zwinne, wolne, otwartoźródłowe narzędzie do zarządzania projektem", "TAG_LINE_2": "Pokochaj swój projekt!", "BLOCK": "Blokuj", - "UNBLOCK": "Odblokuj", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Zablokowane", + "UNBLOCK": "Odblokuj", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Wyjaśnij powód", "CREATED_BY": "Utworzone przez {{fullDisplayName}}", "FROM": "od", "TO": "do", "CLOSE": "zamknij", - "BLOCKED_NOTE": "Dlaczego ta historyjka użytkownika jest zablokowana?", - "BLOCKED_REASON": "Wyjaśnij powód", "GO_HOME": "Zabierz mnie do strony domowej", "PLUGINS": "Wtyczki", "BETA": "Wersja beta", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "zewnętrzny użytkownik", "GENERIC_ERROR": "Umpa Lumpa mówi {{error}}.", "IOCAINE_TEXT": "Czujesz się trochę przytłoczony zadaniem? Daj znać innym klikając na ikonę Iokainy podczas edycji zadania. Jest szansa, że staniesz się odporny na tą (fikcyjną ;) ) śmiertelną truciznę biorąc małe dawki. Z pewnością jednak da Ci ona dodatkowego kopa, który pomoże w pokonaniu nowego wyzwania i staniu się lepszym w tym co robisz!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "UWAGA! Klawisz CAPSLOCK jest aktywny.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Nieprawidłowa wartość", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Nieprzypisane", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Usuń przypisanie", "REMOVE_ASSIGNED": "Ukryj przypisane", "TOO_MANY": "...zbyt wielu użytkowników, filtruj dalej Umpa Lumpy nie ogarniają", "CONFIRM_UNASSIGNED": "Jesteś pewny, że chcesz pozostawić nieprzypisane?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Edytuj przypisanie" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Edytuj przypisanie", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Zamknięte", "OPEN": "Otwórz" }, "WATCHERS": { + "WATCHERS": "Watchers", "ADD": "Dodaj obserwatorów", "TITLE_ADD": "Dodaj członka projektu do listy obserwatorów.", "DELETE": "Usuń obserwatora", @@ -176,7 +184,7 @@ "SAVE": "Zapisz pole niestandardowe", "EDIT": "Edytuj pole niestandardowe", "DELETE": "Usuń niestandardowy atrybut", - "CONFIRM_DELETE": "Pamiętaj, że wszystkie wartości w tym polu zostaną usunięte.
Czy kontynuować?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "filtry", @@ -274,9 +282,9 @@ "HEADER": "Mam już login do Taigi", "PLACEHOLDER_AUTH_NAME": "Login albo e-mail (uwzględnij wielkość liter)", "LINK_FORGOT_PASSWORD": "Zapomniałeś?", - "TITLE_LINK_FORGOT_PASSWORD": "Zapomniałeś hasła?", + "TITLE_LINK_FORGOT_PASSWORD": "Did you forget your password?", "ACTION_ENTER": "Wprowadź", - "ACTION_SIGN_IN": "Zaloguj", + "ACTION_SIGN_IN": "Login", "PLACEHOLDER_AUTH_PASSWORD": "Hasło (uwzględnij wielkość liter)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Strona główna - Taiga", "PAGE_DESCRIPTION": "Główna strona Taiga, z Twoimi głównymi projektami i wszystkimi przypisanymi Tobie i obserwowanymi historyjkami użytkownika, zadaniami i zgłoszeniami.", "EMPTY_WORKING_ON": "Trochę tu pusto?Rozpocznij pracę z Taigą, a pojawią się tutaj historie, zadania oraz zgłoszone błędy, nad którymi pracujesz.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "Nie masz jeszcze żadnych projektów", "WORKING_ON_SECTION": "Pracujesz nad", - "WATCHING_SECTION": "Obserwujesz" + "WATCHING_SECTION": "Obserwujesz", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "Moje projekty - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "załączniki", "TITLE": "{{ plik }} załadowany dnia {{ data }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Wpisz krótki opis", "DEPRECATED": "(przestarzały)", "DEPRECATED_FILE": "Przestarzałe?", "ADD": "Dodaj nowy załącznik. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Maks. rozmiar: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ pokaż przestarzałe załączniki", "HIDE_DEPRECATED": "- ukryj przestarzałe załączniki", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Usuń załącznik...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "załącznik '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Nie udało się usunąć załącznika w związku z następującym błędem: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) plik jest za ciężki, maksymalna wartość to: ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "jest przedawniony" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)", "TAGS": "Tagi", "DESCRIPTION": "Opis", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Projekt publiczny", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Projekt prywatny", - "DELETE": "Usuń ten projekt" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Usuń ten projekt", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Raporty", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Ludzie z którymi pracujesz w Taiga staną się Twoimi kontaktami automatycznie.", "REPORT": "Zgłoś naruszenie", "TABS": { - "ACTIVITY_TAB": "Aktywność", + "ACTIVITY_TAB": "Oś czasu", "ACTIVITY_TAB_TITLE": "Show all the activity of this user", "PROJECTS_TAB": "Projekty", "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projekty", "HELP": "Ustal kolejność Twoich projektów tak, aby na górze znalazły się te najważniejsze.
Pierwsze 10 projektów pojawi się w liście projektów na górnym pasku nawigacji.", "PRIVATE": "Projekt prywatny", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "projekt
punkty", "DEFINED": "zdefiniowane
punkty", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Szukaj w ...", "ACTION_CREATE_PROJECT": "Stwórz projekt", "ACTION_IMPORT_PROJECT": "Importuj projekt", - "SEE_MORE_PROJECTS": "Zobacz więcej projektów", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Utwórz projekt", "TITLE_IMPORT_PROJECT": "Importuj projekt", "TITLE_PRVIOUS_PROJECT": "Pokaż poprzedni projekt", "TITLE_NEXT_PROJECT": "Pokaż kolejne projekty", "HELP_TITLE": "Taiga strona wsparcia", "HELP": "Pomoc", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Prześlij opinię", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Edytuj ustawienia powiadomień", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Tutaj wpisz nowy komentarz", "SHOW_DELETED": "Pokaż usunięty komentarz", "HIDE_DELETED": "Ukryj skasowane komentarze", + "DELETE": "Delete comment", "RESTORE": "Przywróć komentarz" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Admin", "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", "MOVE_US_TO_CURRENT_SPRINT": "Przejdź do bieżącego sprintu", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Pokaż filtry", "SHOW_TAGS": "Pokaż tagi", "EMPTY": "The backlog is empty!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Idź do tablicy zadań użytkownika {{name}}", "NUMBER_SPRINTS": "
sprintów", "EMPTY": "There are no sprints yet", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", "TITLE_ACTION_NEW_SPRINT": "Add new sprint", "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "Obraz zostanie przeskalowany do wielkości 80x80px.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Zmień", - "ACTION_USE_GRAVATAR": "Użyj Gravatara", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Usuń konto Taiga", "CHANGE_EMAIL_SUCCESS": "Sprawdź swoją skrzynkę e-mail!
Wysłaliśmy wiadomość z instrukcjami.", "CHANGE_PHOTO": "Zmień zdjęcie", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Zapomniałeś nad czym pracujesz?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Nie martw się, na Twojej tablicy znajdziesz otwarte zadania, zgłoszenia i historyjki użytkownika w takiej kolejności, w jakiej nad nimi pracowałeś." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "Nowy użytkownik{{username}} dołączył do Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "An external app requires authentication", @@ -1354,6 +1384,12 @@ "CANCEL": "Anuluj" }, "JOYRIDE": { + "NAV": { + "NEXT": "Następny", + "BACK": "Back", + "SKIP": "Skip", + "DONE": "Gotowe!" + }, "DASHBOARD": { "STEP1": { "TITLE": "Twój projekt", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Obserwujesz", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Good luck!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Wszystkie", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Type something...", + "ACTION_TITLE": "Szukaj", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-pt-br.json b/app/locales/taiga/locale-pt-br.json index bb208d14..6f19a2cf 100644 --- a/app/locales/taiga/locale-pt-br.json +++ b/app/locales/taiga/locale-pt-br.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Sim", "NO": "Não", + "OR": "or", "LOADING": "Carregando...", "LOADING_PROJECT": "Carregando o projeto...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Sua ferramenta de código aberto, gratuita e ágil.", "TAG_LINE_2": "AME SEU PROJETO", "BLOCK": "Bloquear", - "UNBLOCK": "Desbloquear", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Bloqueado", + "UNBLOCK": "Desbloquear", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Por favor, explique a razão", "CREATED_BY": "Criado por {{fullDisplayName}}", "FROM": "de", "TO": "para", "CLOSE": "fechar", - "BLOCKED_NOTE": "Por que esta User Story foi bloqueada? ", - "BLOCKED_REASON": "Por favor, explique a razão", "GO_HOME": "Ir ao início", "PLUGINS": "Plugins", "BETA": "Estamos em beta!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "um usuário externo", "GENERIC_ERROR": "Um Oompa Loompas disse {{error}}.", "IOCAINE_TEXT": "Se sentindo sobrecarregado por uma tarefa? Assegure-se de que os outros saibam disso clicando em Iocaine quando estiver editando a tarefa. É possível se tornar imune a essse veneno mortal (fictício) consumindo pequenas quantidades ao longo do tempo, assim como é possível ficar melhor no que faz, ocasionalmente, por assumir desafios extras!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Seja cuidadoso! Você está escrevendo em letras maiúsculas e esse campo é case sensitive, ou seja trata com distinção letras maiúsculas das minúsculas", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Este valor parece ser inválido.", @@ -49,7 +54,7 @@ "TYPE_PHONE": "Este valor deveria ser um número telefônico valido.", "NOTNULL": "Este valor não deveria ser nulo.", "NOT_BLANK": "Esta campo não deveria esta em branco.", - "REQUIRED": "Este valor é obrigatório.", + "REQUIRED": "Este campo é obrigatório.", "REGEXP": "Este valor parece ser inválido.", "MIN": "O valor deve ser maior que ou igual a %s.", "MAX": "Esse valor deve ser menor que ou igual a %s.", @@ -104,7 +109,7 @@ "SEE_USER_PROFILE": "Ver o perfil de {{username }}", "USER_STORY": "User Story", "TASK": "Tarefa", - "ISSUE": "caso", + "ISSUE": "Caso", "TAGS": { "PLACEHOLDER": "To dentro! Me tagueie....", "DELETE": "Apagar tag", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Não assinado", + "ASSIGN": "Atribuir", "DELETE_ASSIGNMENT": "Apagar atribuição", "REMOVE_ASSIGNED": "Remover assinatura", "TOO_MANY": "...muitos usuários, continue filtrando", "CONFIRM_UNASSIGNED": "Você tem certeza que deseja deixar sem ", - "TITLE_ACTION_EDIT_ASSIGNMENT": "E" + "TITLE_ACTION_EDIT_ASSIGNMENT": "E", + "SELF": "Atribuir a mim" }, "STATUS": { "CLOSED": "Fechado", "OPEN": "Aberto" }, "WATCHERS": { + "WATCHERS": "Observadores", "ADD": "Adicionar observadores", "TITLE_ADD": "Adicionar um membro do projeto para a lista de observadores", "DELETE": "Apagar observador", @@ -176,7 +184,7 @@ "SAVE": "Salvar Campo Personalizado", "EDIT": "Editar Campo Personalizado", "DELETE": "Apagar atributo personalizado", - "CONFIRM_DELETE": "Lembre-se que todos os valores deste campo personalizado serão apagados.
Tem certeza que quer continuar?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "filtros", @@ -274,9 +282,9 @@ "HEADER": "Eu já tenho um login Taiga", "PLACEHOLDER_AUTH_NAME": "Nome de usuário ou email (case sensitive)", "LINK_FORGOT_PASSWORD": "Esqueceu?", - "TITLE_LINK_FORGOT_PASSWORD": "Você esqueceu sua senha?", + "TITLE_LINK_FORGOT_PASSWORD": "Esqueceu sua senha?", "ACTION_ENTER": "Entre", - "ACTION_SIGN_IN": "Identifique-se", + "ACTION_SIGN_IN": "Entrar", "PLACEHOLDER_AUTH_PASSWORD": "Senha (case sensitive)" }, "LOGIN_FORM": { @@ -328,7 +336,7 @@ "PLACEHOLDER_NEW_PASSWORD": "Nova senha", "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Redigite a nova senha", "ACTION_RESET_PASSWORD": "Resetar Senha", - "ERROR": "Our Oompa Loompas can't find your request to recover your password. Try to ask for it again.", + "ERROR": "Nossos Oompa Loompas não encontraram a solicitação para recuperar sua senha. Tente solicitar novamente.", "SUCCESS": "Nossos Oompa Loompas salvaram sua nova senha.
Tente identificar-se com ela." }, "INVITATION": { @@ -343,11 +351,12 @@ "HOME": { "PAGE_TITLE": "Início - Taiga", "PAGE_DESCRIPTION": "A página inicial do Taiga contém seus projetos principais e todos as user stories atribuídas ou observadas por você, tarefas e casos", - "EMPTY_WORKING_ON": "It feels empty, doesn't it? Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WORKING_ON": "Isso parece vazio não acha? Comece a trabalhar com Taiga para começar a ver estórias, tarefas e casos em que está trabalhando.", + "EMPTY_WATCHING": "Siga Estórias, Tarefas e Casos nos seus projetos e seja notificado das mudanças :)", "EMPTY_PROJECT_LIST": "Você ainda não tem projetos", "WORKING_ON_SECTION": "Trabalhando em", - "WATCHING_SECTION": "Observando" + "WATCHING_SECTION": "Observando", + "DASHBOARD": "Painel de Projetos" }, "PROJECTS": { "PAGE_TITLE": "Meus projetos - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "anexos", "TITLE": "{{ fileName }} enviado em {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Escreva uma curta descrição", "DEPRECATED": "(obsoleto)", "DEPRECATED_FILE": "Obsoleto?", "ADD": "Adicionar novo anexo. {{maxFileSizeMsg}}", + "DROP": "Jogue os anexos aqui!", "MAX_FILE_SIZE": "[Tamanho Máximo: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ mostrar anexos deprecados", "HIDE_DEPRECATED": "- esconder anexos obsoletos", @@ -371,12 +383,13 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Apagar anexo...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "o anexo '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Não fomos capazes de apagar: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) é muito pesado para nossos Oompa Loompas, tente algo menor que ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "está obsoleto" } }, "PAGINATION": { - "PREVIOUS": "Ant", + "PREVIOUS": "Anterior", "NEXT": "Próximo" }, "ADMIN": { @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Número de pontos US (0 para uma quantidade indeterminada)", "TAGS": "Tags", "DESCRIPTION": "Descrição", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "O que você está procurando?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Projeto Publico", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Projeto Privado", - "DELETE": "Apagar este projeto" + "PRIVATE_PROJECT_DESC": "Por padrão, esse projeto é escondido do público", + "PRIVATE_OR_PUBLIC": "Qual a diferença entre projeto público e privado?", + "DELETE": "Apagar este projeto", + "LOGO_HELP": "A imagem deve ser na escala de 80x80px.", + "CHANGE_LOGO": "Alterar logo", + "ACTION_USE_DEFAULT_LOGO": "Usar imagem padrão" }, "REPORTS": { "TITLE": "Relatórios", @@ -579,7 +601,7 @@ "CANCEL_TITLE": "Cancelar criação", "SET_FIELD_NAME": "Definir nome do seu campo personalizados", "SET_FIELD_DESCRIPTION": "Definir a descrição do seu campo personalizado", - "FIELD_TYPE_DEFAULT": "-- select one --", + "FIELD_TYPE_DEFAULT": "-- selecionar --", "ACTION_UPDATE": "Atualizar campo personalizado", "ACTION_CANCEL_EDITION": "Cancelar edição" }, @@ -591,7 +613,7 @@ "STATUS_ACTIVE": "Ativo", "STATUS_PENDING": "Pendente", "DELETE_MEMBER": "Apagar membro", - "RESEND": "Resend", + "RESEND": "Reenviar", "SUCCESS_SEND_INVITATION": "Enviamos novamente um convite para '{{email}}'.", "ERROR_SEND_INVITATION": "Nós não enviamos o convite.", "SUCCESS_DELETE": "Nós apagamos {{message}}.", @@ -601,23 +623,23 @@ "DEFAULT_VALUES": { "LABEL_POINTS": "Valores padrões para o seletor de pontos", "LABEL_US": "Valor padrão para seletor de status da US", - "LABEL_TASK_STATUS": "Valor padrão para seletor de estatus de tarefa", + "LABEL_TASK_STATUS": "Valor padrão para seletor de status de tarefa", "LABEL_PRIORITY": "Valor padão para seletor de prioridade", "LABEL_SEVERITY": "Valor padrão para seletor de severidade", "LABEL_ISSUE_TYPE": "Valor padrão para seletor de tipo de caso", - "LABEL_ISSUE_STATUS": "Valor padrão para seletor de estatus de caso" + "LABEL_ISSUE_STATUS": "Valor padrão para seletor de status de caso" }, "STATUS": { - "PLACEHOLDER_WRITE_STATUS_NAME": "Digite um nome para o novo estatus" + "PLACEHOLDER_WRITE_STATUS_NAME": "Digite um nome para o novo status" }, "TYPES": { "PLACEHOLDER_WRITE_NAME": "Escreva o nome do novo elemento" }, "US_STATUS": { - "ACTION_ADD_STATUS": "Adicionar novo estatus", + "ACTION_ADD_STATUS": "Adicionar novo status", "IS_ARCHIVED_COLUMN": "Está arquivado?", "WIP_LIMIT_COLUMN": "Limite WIP", - "PLACEHOLDER_WRITE_NAME": "Digite um nome para o novo estatus" + "PLACEHOLDER_WRITE_NAME": "Digite um nome para o novo status" }, "MENU": { "TITLE": "Administrador", @@ -665,10 +687,10 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "As pessoas que trabalahm na Taiga serão seus contatos automaticamente", "REPORT": "Reportar Abuso", "TABS": { - "ACTIVITY_TAB": "Atividade", + "ACTIVITY_TAB": "Linha do Tempo", "ACTIVITY_TAB_TITLE": "Exibir todas as atividade deste usuário", "PROJECTS_TAB": "Projetos", - "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", + "PROJECTS_TAB_TITLE": "Lista de todos os projetos em que o membro esteja envolvido", "LIKES_TAB": "Curtir", "LIKES_TAB_TITLE": "Lista todos curtidas feitas por este usuário", "VOTES_TAB": "Votos", @@ -682,7 +704,7 @@ "PROFILE_SIDEBAR": { "TITLE": "Seu perfil", "DESCRIPTION": "People can see everything you do and what you are working on. Add a nice bio to give an enhanced version of your information.", - "ADD_INFO": "Edit bio" + "ADD_INFO": "Editar biografia" }, "PROFILE_FAVS": { "FILTER_INPUT_PLACEHOLDER": "Digite algo...", @@ -696,7 +718,7 @@ "FILTER_TYPE_TASK_TITLES": "Mostrar apenas tarefas", "FILTER_TYPE_ISSUES": "Casos", "FILTER_TYPE_ISSUES_TITLE": "mostrar apenas problemas", - "EMPTY_TITLE": "It looks like there's nothing to show here." + "EMPTY_TITLE": "Parece que não há nada para exibir aqui." } }, "PROJECT": { @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Projetos", "HELP": "Reordene seus projetos para colocar no topo os mais usados.
Os 10 primeiros projetos aparecerão na lista de projetos da barra de navegação superior.", "PRIVATE": "Projeto Privado", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, um{um fã} outro{# fãs}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, um{um observador} outro{#watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, um{one member} outro{# members}}", "STATS": { "PROJECT": "projetos
pontos", "DEFINED": "pontos
definidos", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Procurar em...", "ACTION_CREATE_PROJECT": "Criar projeto", "ACTION_IMPORT_PROJECT": "Importar projeto", - "SEE_MORE_PROJECTS": "Ver mais projetos", + "MANAGE_PROJECTS": "Gerenciar projetos", "TITLE_CREATE_PROJECT": "Criar projeto", "TITLE_IMPORT_PROJECT": "Importar projeto", "TITLE_PRVIOUS_PROJECT": "Mostrar projetos prévios", "TITLE_NEXT_PROJECT": "Mostrar os próximos projetos", "HELP_TITLE": "Página de Suporte do Taiga", "HELP": "Ajuda", + "HOMEPAGE": "Página pessoal", "FEEDBACK_TITLE": "Enviar feedback", "FEEDBACK": "Feedback", "NOTIFICATIONS_TITLE": "Editar suas configurações de notificações", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Escreva um novo comentário aqui", "SHOW_DELETED": "Mostrar comentários apagados", "HIDE_DELETED": "Esconder comentário apagado", + "DELETE": "Delete comment", "RESTORE": "Restaurar comentário" }, "ACTIVITY": { @@ -948,11 +976,12 @@ "CUSTOMIZE_GRAPH": "Personalize seu gráficos de backlog", "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": "Administrador", - "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", + "CUSTOMIZE_GRAPH_TITLE": "Configure os pontos e sprints pelo Administrador", "MOVE_US_TO_CURRENT_SPRINT": "Mover para Sprint Corrente", + "MOVE_US_TO_LATEST_SPRINT": "ir para o último Sprint", "SHOW_FILTERS": "Mostrar filtros", "SHOW_TAGS": "Exibir tags", - "EMPTY": "The backlog is empty!", + "EMPTY": "O backlog está vazio!", "CREATE_NEW_US": "Criar uma nova EU", "CREATE_NEW_US_EMPTY_HELP": "Você talvez queira criar uma nova user story", "EXCESS_OF_POINTS": "Excesso de pontos", @@ -1012,7 +1041,8 @@ "LINK_TASKBOARD": "Sprint quadro de tarefas", "TITLE_LINK_TASKBOARD": "ir para quadro de tarefas de \"{{name}}\"", "NUMBER_SPRINTS": "
sprints", - "EMPTY": "There are no sprints yet", + "EMPTY": "Ainda não temos sprints.", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "Esse sprint não tem Estórias", "WARNING_EMPTY_SPRINT": "Solte aqui Estórias do seu backlog para iniciar uma nova sprint", "TITLE_ACTION_NEW_SPRINT": "Add nova sprint", "TEXT_ACTION_NEW_SPRINT": "Você poderá querer criar uma nova sprint em seu projeto", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "A imagem será redimensionada para 80x80px", + "IMAGE_HELP": "A imagem será redimensionada para a escala de 80x80px.", "ACTION_CHANGE_IMAGE": "Alterar", - "ACTION_USE_GRAVATAR": "Usar imagem do gravatar", + "ACTION_USE_GRAVATAR": "Usar imagem padrão", "ACTION_DELETE_ACCOUNT": "Apagar conta Taiga", "CHANGE_EMAIL_SUCCESS": "Verifique sua caixa de entrada!
Enviamos um e-mail para sua conta
com as instruções para definir seu novo endereço", "CHANGE_PHOTO": "Mudar foto", @@ -1278,7 +1308,7 @@ "CHOOSE_TEMPLATE_TEXT": "Qual template se encaixa melhor no seu projeto?", "SECTION_TITLE_CREATE_PROJECT": "Criar Projeto", "CREATE_PROJECT_TEXT": "Novo em folha. Tão excitante!", - "PROGRESS_TEMPLATE_SELECTION": "Seleção de template", + "PROGRESS_TEMPLATE_SELECTION": "Seleção de tipo de projeto", "PROGRESS_NAME_DESCRIPTION": "Nome e descrição" }, "WIKI": { @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "Você esqueceu no que está trabalhando?", + "HINT4_TITLE": "Você esqueceu onde está trabalhando?", "HINT4_TEXT": "Não se preocupe, no seu painel você vai encontrar suas tarefas abertas, casos, e user stories na ordem que você trabalha neles." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} ingressou no taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "Um app externo requer autenticação", @@ -1354,10 +1384,16 @@ "CANCEL": "Cancelar" }, "JOYRIDE": { + "NAV": { + "NEXT": "Próximo", + "BACK": "Back", + "SKIP": "Pular", + "DONE": "Terminado" + }, "DASHBOARD": { "STEP1": { "TITLE": "Seu projeto", - "TEXT": "Welcome! Here you will find the projects you are involved on." + "TEXT": "Bem-Vindo! Aqui você encontrará os projetos que você está envolvido." }, "STEP2": { "TITLE": "Trabalhando em", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Observando", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "Você já está trabalhando com Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Boa Sorte!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Descobrir projetos", + "DISCOVER_SUBTITLE": "{projects, plural, um{One public project to discover} outro{# public projects to discover}}", + "MOST_ACTIVE": "Mais ativo", + "MOST_ACTIVE_EMPTY": "Não tem projetos ativos ainda", + "MOST_LIKED": "Mais curtidas", + "MOST_LIKED_EMPTY": "Não existe projetos curtidos ainda", + "VIEW_MORE": "Visualizar mais", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Tudo", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Procurando por pessoas.", + "WEEK": "Última semana", + "MONTH": "Último mês", + "YEAR": "Último ano", + "ALL_TIME": "Todo tempo", + "CLEAR": "Limpar filtros" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Digite algo...", + "ACTION_TITLE": "Procurar", + "RESULTS": "Resultado de pesquisa." + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-ru.json b/app/locales/taiga/locale-ru.json index 238e0ba0..fc476136 100644 --- a/app/locales/taiga/locale-ru.json +++ b/app/locales/taiga/locale-ru.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Да", "NO": "Нет", + "OR": "or", "LOADING": "Пожалуйста, подождите...", "LOADING_PROJECT": "Проект загружается...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "Ваш свободный, agile инструмент управления проектами с открытым исходным кодом", "TAG_LINE_2": "Любит ваши проекты", "BLOCK": "Блокировать", - "UNBLOCK": "Разблокировать", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "Заблокирован", + "UNBLOCK": "Разблокировать", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Пожалуйста разъясните причину", "CREATED_BY": "Создано {{fullDisplayName}}", "FROM": "от", "TO": "к", "CLOSE": "закрыть", - "BLOCKED_NOTE": "Почему эта пользовательская история заблокирована?", - "BLOCKED_REASON": "Пожалуйста разъясните причину", "GO_HOME": "Домой", "PLUGINS": "Плагины", "BETA": "Программа тестируется!", @@ -36,6 +39,8 @@ "EXTERNAL_USER": "внешний пользователь", "GENERIC_ERROR": "Один из Умпа-Лумп говорит {{error}}.", "IOCAINE_TEXT": "Чувствуете, что задание берет верх над вами? Дайте другим знать об этом, нажав на \"Иокаин\", когда редактируете задание. Возможно стать неуязвимым к этому (выдуманному) смертельном яду, потребляя небольшие количества время от времени, так же как возможно стать лучше в том, что вы делаете, временами беря на себя дополнительные препятствия!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", "CAPSLOCK_WARNING": "Будьте внимательны! Введённый текст состоит из заглавных букв, а регистр имеет значение.", "FORM_ERRORS": { "DEFAULT_MESSAGE": "Кажется, это значение некорректно.", @@ -139,17 +144,20 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "Не назначен", + "ASSIGN": "Assign", "DELETE_ASSIGNMENT": "Удалить назначение", "REMOVE_ASSIGNED": "Удалить назначение", "TOO_MANY": "...слишком много пользователей, продолжайте фильтровать", "CONFIRM_UNASSIGNED": "Вы уверены что не хотите назначить ответственного?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "Изменить назначение" + "TITLE_ACTION_EDIT_ASSIGNMENT": "Изменить назначение", + "SELF": "Assign to me" }, "STATUS": { "CLOSED": "Закрыт", "OPEN": "Открыть" }, "WATCHERS": { + "WATCHERS": "Наблюдатели", "ADD": "Добавить наблюдателей", "TITLE_ADD": "Добавить участника проекта к списку наблюдателей", "DELETE": "Удалить наблюдателя", @@ -176,7 +184,7 @@ "SAVE": "Сохранить поле", "EDIT": "Редактировать поле", "DELETE": "Удалить атрибут", - "CONFIRM_DELETE": "Не забудьте, что все значения для этого специального поля будут удалены.
Вы действительно хотите продолжить?" + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" }, "FILTERS": { "TITLE": "фильтры", @@ -274,9 +282,9 @@ "HEADER": "У меня уже есть логин в Taiga", "PLACEHOLDER_AUTH_NAME": "Логин или email (с учетом регистра)", "LINK_FORGOT_PASSWORD": "Забыли?", - "TITLE_LINK_FORGOT_PASSWORD": "Вы забыли пароль?", + "TITLE_LINK_FORGOT_PASSWORD": "Вы забыли свой пароль?", "ACTION_ENTER": "Ввод", - "ACTION_SIGN_IN": "Войти", + "ACTION_SIGN_IN": "Логин", "PLACEHOLDER_AUTH_PASSWORD": "Пароль (чувствителен к регистру)" }, "LOGIN_FORM": { @@ -344,10 +352,11 @@ "PAGE_TITLE": "Домашняя страница - Taiga", "PAGE_DESCRIPTION": "Главная страница Taiga с вашими основными проектами, назначенными и отслеживаемыми ПИ, задачами и запросами", "EMPTY_WORKING_ON": "Пустовато, не правда ли? Начните работать в Taiga - и тут появятся ПИ, задачи и запросы, над которыми вы работаете.", - "EMPTY_WATCHING": "Следите за ПИ, задачами, запросами... о которых хотите знать :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "У Вас пока нет проектов", "WORKING_ON_SECTION": "Работает над", - "WATCHING_SECTION": "Отслеживаемые" + "WATCHING_SECTION": "Отслеживаемые", + "DASHBOARD": "Projects Dashboard" }, "PROJECTS": { "PAGE_TITLE": "Мои проекты", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "Вложения", "TITLE": "{{ fileName }} загружен {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", "DESCRIPTION": "Введите краткое описание", "DEPRECATED": "(устаревший)", "DEPRECATED_FILE": "Устаревший?", "ADD": "Добавить вложение. {{maxFileSizeMsg}}", + "DROP": "Drop attachments here!", "MAX_FILE_SIZE": "[Макс. размер: {{maxFileSize}}]", "SHOW_DEPRECATED": "Показать устаревшие приложения", "HIDE_DEPRECATED": "- спрятать устаревшие приложения", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Удалить вложение...", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "приложение '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "Мы не смогли провести удаление: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) это слишком много для наших Умпа-Лумп, попробуйте еще раз с размером меньшим, чем ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "рекомендовано" } @@ -440,9 +453,18 @@ "NUMBER_US_POINTS": "Количество очков ПИ (0 - неопределенное количество)", "TAGS": "Тэги", "DESCRIPTION": "Описание", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", "PUBLIC_PROJECT": "Публичный проект", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", "PRIVATE_PROJECT": "Закрытый проект", - "DELETE": "Удалить проект" + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Удалить проект", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" }, "REPORTS": { "TITLE": "Отчеты", @@ -665,7 +687,7 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Люди, с которыми вы работаете в Taiga, будут отмечены как ваши контакты автоматически", "REPORT": "Пожаловаться", "TABS": { - "ACTIVITY_TAB": "Действия", + "ACTIVITY_TAB": "График работ", "ACTIVITY_TAB_TITLE": "Показать все действия пользователя", "PROJECTS_TAB": "Проекты", "PROJECTS_TAB_TITLE": "Список всех проектов, в которых участвует пользователь", @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "Проекты", "HELP": "Реорганизуйте свои проекты так чтобы часто используемые были бы наверху.
Первые 10 проектов будут находится вверху списка проектов.", "PRIVATE": "Закрытый проект", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{один фанат} other{# фаната (-ов)}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{один наблюдатель} other{# наблюдателя (-ей)}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "проекта
очки", "DEFINED": "заданные
очки", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "Искать в...", "ACTION_CREATE_PROJECT": "Создать проект", "ACTION_IMPORT_PROJECT": "Импортировать проект", - "SEE_MORE_PROJECTS": "Смотрите другие проекты", + "MANAGE_PROJECTS": "Manage projects", "TITLE_CREATE_PROJECT": "Создать проект", "TITLE_IMPORT_PROJECT": "Импортировать проект", "TITLE_PRVIOUS_PROJECT": "Показать предыдущие проекты", "TITLE_NEXT_PROJECT": "Показать следующие проекты", "HELP_TITLE": "Страница поддержки Taiga", "HELP": "Помощь", + "HOMEPAGE": "Homepage", "FEEDBACK_TITLE": "Оставить отзыв", "FEEDBACK": "Обратная связь", "NOTIFICATIONS_TITLE": "Настроить уведомления", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "Добавить комментарий", "SHOW_DELETED": "Показать удаленный комментарий", "HIDE_DELETED": "Скрыть удаленный комментарий", + "DELETE": "Delete comment", "RESTORE": "Показать удаленный комментарий" }, "ACTIVITY": { @@ -950,6 +978,7 @@ "CUSTOMIZE_GRAPH_ADMIN": "Админка", "CUSTOMIZE_GRAPH_TITLE": "Настройте очки и спринты в админке", "MOVE_US_TO_CURRENT_SPRINT": "Перейти к текущему спринту", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", "SHOW_FILTERS": "Показать фильтры", "SHOW_TAGS": "Показать теги", "EMPTY": "Список задач пуст!", @@ -1013,6 +1042,7 @@ "TITLE_LINK_TASKBOARD": "Перейти к Панели Задач \"{{name}}\"", "NUMBER_SPRINTS": "
спринты", "EMPTY": "Спринтов пока что нет", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", "WARNING_EMPTY_SPRINT": "Накидайте сюда ПИ из списка задач чтобы начать новый спринт", "TITLE_ACTION_NEW_SPRINT": "Добавить новый спринт", "TEXT_ACTION_NEW_SPRINT": "Вы можете создать новый спринт в проекте", @@ -1254,9 +1284,9 @@ } }, "USER_PROFILE": { - "IMAGE_HELP": "Изображение будет отмасштабировано до 80x80px.
", + "IMAGE_HELP": "The image will be scaled to 80x80px.", "ACTION_CHANGE_IMAGE": "Изменить", - "ACTION_USE_GRAVATAR": "Испольвать аватар из gravatar", + "ACTION_USE_GRAVATAR": "Use default image", "ACTION_DELETE_ACCOUNT": "Удалить аккаунт", "CHANGE_EMAIL_SUCCESS": "Проверьте входящие письма!
Мы послали письмо на ваш аккаунт
с инструкциями по установке вашего нового адреса.", "CHANGE_PHOTO": "Изменить фото", @@ -1308,7 +1338,7 @@ "HINT2_TEXT": "Теперь команды могут создавать специальные поля, чтобы гибко вводить данные, специфические для их рабочего процесса.", "HINT3_TITLE": "Перестройте список своих проектов так, чтобы выделить самые важные для себя.", "HINT3_TEXT": "Первые 10 проектов доступны из панели прямого доступа сверху.", - "HINT4_TITLE": "Вы забыли над чем работали?", + "HINT4_TITLE": "Did you forget what were you working on?", "HINT4_TEXT": "Не переживайте, на вашем рабочем столе вы найдёте ваши активные задачи, запросы и пользовательские истории в том порядке, в котором вы над ними работали." }, "TIMELINE": { @@ -1343,7 +1373,7 @@ "NEW_USER": "{{username}} присоединился к Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Кликая \"Зарегистрироваться\"', вы соглашаетесь с нашими
условиями обслуживания и политикой обработки персональных данных." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." }, "EXTERNAL_APP": { "PAGE_TITLE": "Внешнее приложение требующие аутентификации", @@ -1354,6 +1384,12 @@ "CANCEL": "Отмена" }, "JOYRIDE": { + "NAV": { + "NEXT": "Следующий", + "BACK": "Бэкенд разработчик", + "SKIP": "Skip", + "DONE": "Завершена" + }, "DASHBOARD": { "STEP1": { "TITLE": "Ваш проект", @@ -1365,7 +1401,7 @@ }, "STEP3": { "TITLE": "Отслеживаемое", - "TEXT1": "And right here you will find the ones that you want to know about.", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", "TEXT2": "You are already working with Taiga ;)" }, "STEP4": { @@ -1408,5 +1444,33 @@ "TEXT2": "Удачи!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Все", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Введите что-нибудь...", + "ACTION_TITLE": "Поиск", + "RESULTS": "Search results" + } } } \ No newline at end of file diff --git a/app/locales/taiga/locale-sv.json b/app/locales/taiga/locale-sv.json new file mode 100644 index 00000000..73ffabf5 --- /dev/null +++ b/app/locales/taiga/locale-sv.json @@ -0,0 +1,1476 @@ +{ + "COMMON": { + "YES": "Ja", + "NO": "Nej", + "OR": "or", + "LOADING": "Laddar ...", + "LOADING_PROJECT": "Laddar projekt...", + "DATE": "YYYY-MM-DD", + "DATETIME": "YYYY-MM-DD HH:mm", + "SAVE": "Spara", + "CANCEL": "Avbryt", + "ACCEPT": "Acceptera", + "DELETE": "Radera", + "CREATE": "Skapa", + "ADD": "Lägg till", + "COPY_TO_CLIPBOARD": "Kopiera till urklipp: Ctrl+C", + "EDIT": "Redigera", + "DRAG": "Dra", + "TAG_LINE": "Ditt agila, gratis och öppen-källkod projekthanteringsverktyg", + "TAG_LINE_2": "Älska ditt projekt", + "BLOCK": "Blockera", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", + "BLOCKED": "Blockerad", + "UNBLOCK": "Avblockera", + "UNBLOCK_TITLE": "Unblock this item", + "BLOCKED_NOTE": "Why is this blocked?", + "BLOCKED_REASON": "Vänligen förklara orsaken", + "CREATED_BY": "Skapad av {{fullDisplayName}}", + "FROM": "från", + "TO": "till ", + "CLOSE": "stäng", + "GO_HOME": "Ta mig hem", + "PLUGINS": "Insticksprogram", + "BETA": "Vi betatestar!", + "ONE_ITEM_LINE": "En post per rad ...", + "NEW_BULK": "Lägg till flera nya", + "RELATED_TASKS": "Besläktade uppgifter", + "LOGOUT": "Logg ut", + "EXTERNAL_USER": "en extern användare", + "GENERIC_ERROR": "En av våra Oompa Loompier säger {{error}}.", + "IOCAINE_TEXT": "Känner du dig lite bortkommen med en uppgift? Försäkra dig om att andra känner till uppgiften när du klickar på Iocaine-knappen när du ändrar uppgiften. Det är möjligt att bli immun till det här (påhittade) dödliga giftet om du tar små mängder över tid - och du kan även så småningom om bli bättre på vad du gör när du då och då tar på dig större utmaningar!", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", + "CAPSLOCK_WARNING": "Var försiktig! Du skriver med STORA bokstäver och det här fältet är skriftlägekänsligt.", + "FORM_ERRORS": { + "DEFAULT_MESSAGE": "Det här värdet är felaktigt. ", + "TYPE_EMAIL": "Värdet måste vara en giltig e-postadress", + "TYPE_URL": "Det här värdet borde vara en giltig länk", + "TYPE_URLSTRICT": "Det här värdet borde vara en giltig länk", + "TYPE_NUMBER": "Det här värdet måste vara ett giltigt nummer", + "TYPE_DIGITS": "Det här värdet måste vara ett siffer.", + "TYPE_DATEISO": "Värdet ska vara ett giltigt datum (YYYY-MM-DD).", + "TYPE_ALPHANUM": "Det här värdet ska vara alfanumeriskt", + "TYPE_PHONE": "Det här värdet ska vara ett giltigt telefonnummer", + "NOTNULL": "Det här värdet kan inte vara null.", + "NOT_BLANK": "Det här värdet ska inte vara blankt. ", + "REQUIRED": "Det här värdet är obligatoriskt.", + "REGEXP": "Det här värdet är felaktigt. ", + "MIN": "Det här värdet skulle vara större eller lika med %s.", + "MAX": "Det här värdet ska vara lägre eller lika med %s. ", + "RANGE": "Värdet ska vara mellan %s och %s. ", + "MIN_LENGTH": "Det här värdet är för kort. Det ska ha %s tecken eller flera. ", + "MAX_LENGTH": "Det här värdet är för långt. Du kan ha %s tecken eller mindre. ", + "RANGE_LENGTH": "Värdets längd är ej giltig. Det ska vara mellan %s och %s tecken långt. ", + "MIN_CHECK": "Du måste välja minst %s val.", + "MAX_CHECK": "Du måste välja %s val eller mindre.", + "RANGE_CHECK": "Du måste välja mellan %s och %s val.", + "EQUAL_TO": "Det här värdet skulle vara det samma. " + }, + "PICKERDATE": { + "FORMAT": "YYYY-MM-DD", + "IS_RTL": "falsk", + "FIRST_DAY_OF_WEEK": "1", + "PREV_MONTH": "Tidigare månad", + "NEXT_MONTH": "Nästa månad", + "MONTHS": { + "JAN": "Januar", + "FEB": "Februar", + "MAR": "Mars", + "APR": "April", + "MAY": "Maj", + "JUN": "Juni", + "JUL": "Juli", + "AUG": "Augusti", + "SEP": "September", + "OCT": "Oktober", + "NOV": "November", + "DEC": "December" + }, + "WEEK_DAYS": { + "SUN": "Söndag", + "MON": "Måndag", + "TUE": "Tisdag", + "WED": "Onsdag", + "THU": "Torsdag", + "FRI": "Fredag", + "SAT": "Lördag" + }, + "WEEK_DAYS_SHORT": { + "SUN": "Sol", + "MON": "Mån", + "TUE": "Tis", + "WED": "Ons", + "THU": "Tor", + "FRI": "Fre", + "SAT": "Lör" + } + }, + "SEE_USER_PROFILE": "Se {{username }}s profil", + "USER_STORY": "Användarhistorie", + "TASK": "Uppgift", + "ISSUE": "ärende", + "TAGS": { + "PLACEHOLDER": "Det är jag! Tagga mig ...", + "DELETE": "Ta bort etikett", + "ADD": "Lägg till etikett" + }, + "DESCRIPTION": { + "EMPTY": "Så tråkigt med ingen information ... här kan du beskriva det ...", + "NO_DESCRIPTION": "Ingen beskrivning än" + }, + "FIELDS": { + "SUBJECT": "Titel", + "NAME": "Namn", + "URL": "Länk", + "DESCRIPTION": "Beskrivning", + "VALUE": "Värde", + "SLUG": "Slugg", + "COLOR": "Färg", + "IS_CLOSED": "Är den stängd?", + "STATUS": "Status", + "TYPE": "Typ", + "SEVERITY": "Allvarsgrad", + "PRIORITY": "Prioritet", + "ASSIGNED_TO": "Tilldelad till", + "POINTS": "Poäng", + "BLOCKED_NOTE": "blockerad notering", + "IS_BLOCKED": "är blockerad", + "REF": "Ref. ", + "VOTES": "Röster" + }, + "ROLES": { + "ALL": "Alla" + }, + "ASSIGNED_TO": { + "NOT_ASSIGNED": "Ej tilldelad", + "ASSIGN": "Assign", + "DELETE_ASSIGNMENT": "Ta bort tilldelning", + "REMOVE_ASSIGNED": "Ta bort tilldelning", + "TOO_MANY": "... för många användare, fortsätter filtreringen", + "CONFIRM_UNASSIGNED": "Vill du lämna det utan att tilldela det? ", + "TITLE_ACTION_EDIT_ASSIGNMENT": "Redigera tilldelning", + "SELF": "Assign to me" + }, + "STATUS": { + "CLOSED": "Stängd", + "OPEN": "Öppen" + }, + "WATCHERS": { + "WATCHERS": "Observatörer", + "ADD": "Lägg till bevakare", + "TITLE_ADD": "Lägg till en projektmedlem till bevakningslistan", + "DELETE": "Ta bort observatör", + "TITLE_LIGHTBOX_DELETE_WARTCHER": "Ta bort observatör ..." + }, + "WATCH_BUTTON": { + "WATCH": "Visa", + "WATCHING": "Bevakar", + "UNWATCH": "Frånkoppla visning", + "WATCHERS": "Observatörer", + "BUTTON_TITLE": "Visa/Visa inte den här posten", + "COUNTER_TITLE": "{total, plural, en{one watcher} andra{# watchers}}" + }, + "VOTE_BUTTON": { + "UPVOTE": "Rösta för", + "UPVOTED": "Rösta för", + "DOWNVOTE": "Rösta emot", + "VOTERS": "Röstande", + "BUTTON_TITLE": "Rösta för / Rösta emot den här posten", + "COUNTER_TITLE": "{total, plural, one{one vote} other{# votes}}" + }, + "CUSTOM_ATTRIBUTES": { + "CUSTOM_FIELDS": "Anpassade fält", + "SAVE": "Spara anpassad fält", + "EDIT": "Ändra anpassad fält", + "DELETE": "Ta bort anpassad egenskap", + "CONFIRM_DELETE": "Remeber that all values in this custom field will be deleted.\n Are you sure you want to continue?" + }, + "FILTERS": { + "TITLE": "filter", + "INPUT_PLACEHOLDER": "Titel eller referens", + "TITLE_ACTION_FILTER_BUTTON": "sök", + "BREADCRUMB_TITLE": "tillbaka till kategorierna", + "BREADCRUMB_FILTERS": "Filter", + "BREADCRUMB_STATUS": "status" + }, + "WYSIWYG": { + "H1_BUTTON": "Första nivån snart klar", + "H1_SAMPLE_TEXT": "Din titel här", + "H2_BUTTON": "Andra nivåns rubrik", + "H2_SAMPLE_TEXT": "Din titel här", + "H3_BUTTON": "Tredje nivåns rubrik", + "H3_SAMPLE_TEXT": "Din titel här", + "BOLD_BUTTON": "Fet", + "BOLD_BUTTON_SAMPLE_TEXT": "Din text här", + "ITALIC_BUTTON": "Kursiv", + "ITALIC_SAMPLE_TEXT": "Din text här", + "STRIKE_BUTTON": "Slag", + "STRIKE_SAMPLE_TEXT": "Din text här", + "BULLETED_LIST_BUTTON": "Punktlista", + "BULLETED_LIST_SAMPLE_TEXT": "Din text här", + "NUMERIC_LIST_BUTTON": "Numrerad lista", + "NUMERIC_LIST_SAMPLE_TEXT": "Din text här", + "PICTURE_BUTTON": "Bild", + "PICTURE_SAMPLE_TEXT": "Din alternativa text till bilden här ...", + "LINK_BUTTON": "Länk", + "LINK_SAMPLE_TEXT": "Din text till länken här ...", + "QUOTE_BLOCK_BUTTON": "Citatblock", + "QUOTE_BLOCK_SAMPLE_TEXT": "Din text här", + "CODE_BLOCK_BUTTON": "Kodblock", + "CODE_BLOCK_SAMPLE_TEXT": "Din text här", + "PREVIEW_BUTTON": "Förhandsvisa", + "EDIT_BUTTON": "Redigera", + "MARKDOWN_HELP": "Hjälp för markeringssyntax" + }, + "PERMISIONS_CATEGORIES": { + "SPRINTS": { + "NAME": "Sprintar", + "VIEW_SPRINTS": "Visa sprintar", + "ADD_SPRINTS": "Lägg till sprintar", + "MODIFY_SPRINTS": "Ändra sprintar", + "DELETE_SPRINTS": "Ta bort sprintar" + }, + "USER_STORIES": { + "NAME": "Användarhistorie", + "VIEW_USER_STORIES": "Visa användarhistorier", + "ADD_USER_STORIES": "Lägg till användarhistorier", + "MODIFY_USER_STORIES": "Modifiera användarhistorier", + "DELETE_USER_STORIES": "Ta bort användarhistorier" + }, + "TASKS": { + "NAME": "Uppgift", + "VIEW_TASKS": "Visa uppgifter", + "ADD_TASKS": "Lägg till uppgifter", + "MODIFY_TASKS": "Modifiera uppgifter", + "DELETE_TASKS": "Ta bort uppgift" + }, + "ISSUES": { + "NAME": "Ärenden", + "VIEW_ISSUES": "Visa ärenden", + "ADD_ISSUES": "Lägg till ärenden", + "MODIFY_ISSUES": "Modifiera ärenden", + "DELETE_ISSUES": "Ta bort uppgifter" + }, + "WIKI": { + "NAME": "Wiki", + "VIEW_WIKI_PAGES": "Visa wiki-sidor", + "ADD_WIKI_PAGES": "Lägg till wiki-sida", + "MODIFY_WIKI_PAGES": "Modifiera wiki-sidor", + "DELETE_WIKI_PAGES": "Ta bort wiki-sidor", + "VIEW_WIKI_LINKS": "Visa wiki-länkar", + "ADD_WIKI_LINKS": "Lägg till wiki-länkar", + "DELETE_WIKI_LINKS": "Ta bort wiki-länkar" + } + }, + "META": { + "PAGE_TITLE": "Taiga", + "PAGE_DESCRIPTION": "Taiga är ett projekthanteringsverktyg för nystartade företag och agila utvecklings- och designteam som behöver enkla, vackra verktyg som det är trevligt att jobba med. " + } + }, + "LOGIN": { + "PAGE_TITLE": "Logga in - Taiga", + "PAGE_DESCRIPTION": "Loggar in till Taiga, en projekthanteringsplatform för startups och agila utvecklare & designers som vill ha ett simpelt, vackert verktyg som gör arbetet till en fröjd." + }, + "AUTH": { + "INVITED_YOU": "har bjudit in dig till projektet", + "NOT_REGISTERED_YET": "Inte registrerad ännu?", + "REGISTER": "Registrera", + "CREATE_ACCOUNT": "skapa ett gratis konto här" + }, + "LOGIN_COMMON": { + "HEADER": "Jag har redan ett Taiga-konto", + "PLACEHOLDER_AUTH_NAME": "Användarnamn eller e-postadress (skiftlägeskänsligt)", + "LINK_FORGOT_PASSWORD": "Glömt det?", + "TITLE_LINK_FORGOT_PASSWORD": "Har du glömt ditt lösenord?", + "ACTION_ENTER": "Gå in", + "ACTION_SIGN_IN": "Logga in", + "PLACEHOLDER_AUTH_PASSWORD": "Lösenord (skiftlägeskänslig)" + }, + "LOGIN_FORM": { + "ERROR_AUTH_INCORRECT": "Enligt våra Ooma Loompas är ditt användarnamn, din e-postadress eller ditt lösenord inkorrekt.", + "SUCCESS": "Våra Ooma Loompas är glada, välkommen till Taiga!" + }, + "REGISTER": { + "PAGE_TITLE": "Registrera - Taiga", + "PAGE_DESCRIPTION": "Skapa ditt konto i Taiga, en projekthanteringsplatform för startups och agila utvecklare & designers som vill ha ett simpelt, vackert verktyg som gör arbetet till en fröjd." + }, + "REGISTER_FORM": { + "TITLE": "Registrera ett nytt Taiga-konto (gratis)", + "PLACEHOLDER_NAME": "Välj ett användarnamn (skiflägeskänsligt)", + "PLACEHOLDER_FULL_NAME": "Ange ditt fullständiga namn", + "PLACEHOLDER_EMAIL": "Din e-postadress", + "PLACEHOLDER_PASSWORD": "Välj ett lösenord (skiftlägeskänsligt)", + "ACTION_SIGN_UP": "Registrera", + "TITLE_LINK_LOGIN": "Logga in", + "LINK_LOGIN": "Redan registrerad? Logg in" + }, + "FORGOT_PASSWORD": { + "PAGE_TITLE": "Glömt lösenord - Taiga", + "PAGE_DESCRIPTION": "Skriv ditt användarnamn eller e-postadress för att få ett nytt lösenord så att du kan få åtkomst till Taiga igen. " + }, + "FORGOT_PASSWORD_FORM": { + "TITLE": "Oj, har du glömt ditt lösenord?", + "SUBTITLE": "Skriv ditt användarnamn eller e-postadress för att få ett nytt. ", + "PLACEHOLDER_FIELD": "Användarnamn eller e-postadress", + "ACTION_RESET_PASSWORD": "Återställ lösenord", + "LINK_CANCEL": "Nej, ta mig tillbaka. Jag tror att jag minns det.", + "SUCCESS": "Kolla inboxen!
Du ska ha fått ett e-postmeddelande med instruktioner för hur du ändrar lösenordet", + "ERROR": "Enligt våra Oompaloompier är du inte registrerad ännu." + }, + "CHANGE_PASSWORD": { + "PAGE_TITLE": "Byt ditt lösenord - Taiga", + "PAGE_DESCRIPTION": "Skapa ett nytt lösenord för din Taiga-konto och hej! du kanske behöver äta något järnrik kost. Det är bra för hjärnan. :P", + "SECTION_NAME": "Byt lösenord", + "FIELD_CURRENT_PASSWORD": "Befintligt lösenord", + "PLACEHOLDER_CURRENT_PASSWORD": "Ditt nuvarande lösenord (eller tomt om du inte har något lösenord än)", + "FIELD_NEW_PASSWORD": "Nytt lösenord", + "PLACEHOLDER_NEW_PASSWORD": "Skriv in lösenordet på nytt", + "FIELD_RETYPE_PASSWORD": "Skriv in lösenordet på nytt", + "PLACEHOLDER_RETYPE_PASSWORD": "Skriv in ditt nya lösenord på nytt", + "ERROR_PASSWORD_MATCH": "Lösenordet överensstämmer inte. " + }, + "CHANGE_PASSWORD_RECOVERY_FORM": { + "TITLE": "Skapa ett nytt Taiga-lösenord", + "SUBTITLE": "Vet du va? Du kanske behöver äta mera järnrika livsmedel. Det är bra för hjärnan :P", + "PLACEHOLDER_NEW_PASSWORD": "Nytt lösenord", + "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Skriv in lösenordet på nytt", + "ACTION_RESET_PASSWORD": "Återställ lösenord", + "ERROR": "Våra Oompa Loompier kan inte hitta din begäran om att återställa lösenordet. Fråga om det på nytt.", + "SUCCESS": "Våra Oompa Loompier har sparat ditt nya lösenord
Försök att logga in med det. " + }, + "INVITATION": { + "PAGE_TITLE": "Invitation accepterad - Taiga", + "PAGE_DESCRIPTION": "Acceptera invitationer för att bli medlem i ett Taiga-projekt. Taiga är ett projekthanteringsverktyg för uppstartsföretag och agila utvecklare och designers som behöver ett enkelt, vackert verktyg som är mycket trevligt att använda. " + }, + "INVITATION_LOGIN_FORM": { + "NOT_FOUND": "Våra Oompa Loompier kan inte hitta din invitation.", + "SUCCESS": "Du har nu blitt medlem i det här projektet. Välkommen till {{project_name}}", + "ERROR": "Enligt våra Oompa Loompier är du inte registrerad än eller ditt lösenord är fel. " + }, + "HOME": { + "PAGE_TITLE": "Hem - Taiga", + "PAGE_DESCRIPTION": "Taiga hemsida med dina viktigaste projekt, alla dina tilldelade användaruppgifter, uppdrag och frågor", + "EMPTY_WORKING_ON": "Känns tråkigt utan innehåll, eller hur?` Börja att arbeta med Taiga och du vill se uppgifter, uppdrag och ärenden du arbetar med. ", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", + "EMPTY_PROJECT_LIST": "Du har inte än några projekt", + "WORKING_ON_SECTION": "Arbetar med", + "WATCHING_SECTION": "Bevakar", + "DASHBOARD": "Projects Dashboard" + }, + "PROJECTS": { + "PAGE_TITLE": "Mina projekt - Taiga", + "PAGE_DESCRIPTION": "En lista med alla dina projekt som du kan organisera eller skapa ett nytt. ", + "MY_PROJECTS": "Mina projekt" + }, + "ATTACHMENT": { + "SECTION_NAME": "bilagor", + "TITLE": "{{ fileName }} laddat upp {{ date }}", + "LIST_VIEW_MODE": "List view mode", + "GALLERY_VIEW_MODE": "Gallery view mode", + "DESCRIPTION": "Skriv en kort beskrivning", + "DEPRECATED": "(borttagen)", + "DEPRECATED_FILE": "Borttagen?", + "ADD": "Lägg till ny bilaga. {{maxFileSizeMsg}}", + "DROP": "Drag och släpp filer som ska bifogas här!", + "MAX_FILE_SIZE": "[Max. size: {{maxFileSize}}]", + "SHOW_DEPRECATED": "+ visa ogillade bilagor", + "HIDE_DEPRECATED": "- dölj ogillade bilagor", + "COUNT_DEPRECATED": "({{ counter }} deprecated)", + "MAX_UPLOAD_SIZE": "Maximal filstorlek att ladda upp är {{maxFileSize}}", + "DATE": "DD MM YYYY [at] hh:mm", + "ERROR_UPLOAD_ATTACHMENT": "Det var omöjligt för oss att ladda upp '{{fileName}}'. {{errorMessage}}", + "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Ta bort bilagan ...", + "MSG_LIGHTBOX_DELETE_ATTACHMENT": "bifogad '{{fileName}}'", + "ERROR_DELETE_ATTACHMENT": "Vi klarade inte att ta bort: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) är för tungt för våra Oompa Loompier. Försök med något mindre än ({{maxFileSize}})", + "FIELDS": { + "IS_DEPRECATED": "undviks" + } + }, + "PAGINATION": { + "PREVIOUS": "Förra", + "NEXT": "Nästa" + }, + "ADMIN": { + "COMMON": { + "TITLE_ACTION_EDIT_VALUE": "Redigera", + "TITLE_ACTION_DELETE_VALUE": "Ta bort" + }, + "HELP": "Behöver du hjälp? Besök hjälpsidorna!", + "PROJECT_DEFAULT_VALUES": { + "TITLE": "Standardvärden", + "SUBTITLE": "Sätt standardvärden för alla ingångsval." + }, + "MEMBERSHIPS": { + "TITLE": "Hantera medlemmar", + "PAGE_TITLE": "Medlemskap - {{projectName}}", + "ADD_BUTTON": "+ Ny medlem", + "ADD_BUTTON_TITLE": "Lägg till ny medlem" + }, + "PROJECT_EXPORT": { + "TITLE": "Exportera", + "SUBTITLE": "Exportera ditt projekt som säkerhetskopia eller för att skapa ett nytt projekt baserad på det här projektet. ", + "EXPORT_BUTTON": "Exportera", + "EXPORT_BUTTON_TITLE": "Exportera dina projekt", + "LOADING_TITLE": "Vi skapar en hämtningsfil för dig. ", + "DUMP_READY": "Din hämtningsfil är klar!", + "LOADING_MESSAGE": "Vänligen stäng inte den här sidan. ", + "ASYNC_MESSAGE": "Vi vill skicka dig ett e-postmeddelande när vi är klara.", + "SYNC_MESSAGE": "Om hämtningen inte startar automatiskt, klick här.", + "ERROR": "Våra Oompa Loompier har vissa problemer med att skapa din hämtningsfil. Vänligen försök på nytt!", + "ERROR_BUSY": "Beklagar. Våra oompa loompier är mycket upptagna. Vänligen försök på nytt om några minuter. ", + "ERROR_MESSAGE": "Våra Oompa Loompier har vissa problem med att skapa din hämtfil: {{message}}" + }, + "MODULES": { + "TITLE": "Moduler", + "ENABLE": "Aktivera", + "DISABLE": "Avvaktivera", + "BACKLOG": "Inkorg", + "BACKLOG_DESCRIPTION": "Hantera dina användarhistorier för att organisera visningar av kommande och prioriterade jobb. ", + "KANBAN": "Kanban", + "KANBAN_DESCRIPTION": "Organisera dina projekt med Lean med den här tavlan. ", + "ISSUES": "Frågor", + "ISSUES_DESCRIPTION": "Sök efter buggar, visa frågor eller gör förbättringar relaterad till dina projekt. Missa inte något!", + "WIKI": "Wiki", + "WIKI_DESCRIPTION": "Lägg till, modifiera eller ta bort innehåll i samarbetet med andra. Det här är rätt plats för ditt projektdokumentation. ", + "MEETUP": "Möt upp", + "MEETUP_DESCRIPTION": "Välj ditt videokonferenssystem. Även utvecklare behöver mötas ansikte mot ansikte.", + "SELECT_VIDEOCONFERENCE": "Välj videokonferenssystem", + "SALT_CHAT_ROOM": "Om du önskar det kan du lägga till några tillfälliga tecken till chat-rummets namn. ", + "JITSI_CHAT_ROOM": "Jitsi", + "APPEARIN_CHAT_ROOM": "Dyker upp i ", + "TALKY_CHAT_ROOM": "Talky", + "CUSTOM_CHAT_ROOM": "Anpassa", + "URL_CHAT_ROOM": "Länken till ditt chattrum" + }, + "PROJECT_PROFILE": { + "PAGE_TITLE": "{{sectionName}} - Projektprofil - {{projectName}}", + "PROJECT_DETAILS": "Projektdetaljer", + "PROJECT_NAME": "Projektnamn", + "PROJECT_SLUG": "Projektslugg", + "NUMBER_SPRINTS": "Antal sprintar (0 för en obestämd kvantitet)", + "NUMBER_US_POINTS": "Antal US-poäng (0 för ett obestämd antal)", + "TAGS": "Etiketter", + "DESCRIPTION": "Beskrivning", + "RECRUITING": "Is this project looking for people?", + "RECRUITING_MESSAGE": "Who are you looking for?", + "RECRUITING_PLACEHOLDER": "Define the profiles you are looking for", + "PUBLIC_PROJECT": "Publika projekt", + "PUBLIC_PROJECT_DESC": "Users will be able to find and view your project", + "PRIVATE_PROJECT": "Privata projekt", + "PRIVATE_PROJECT_DESC": "By default, this project will be hidden to the public", + "PRIVATE_OR_PUBLIC": "What's the difference between public and private projects?", + "DELETE": "Ta bort projekt", + "LOGO_HELP": "The image will be scaled to 80x80px.", + "CHANGE_LOGO": "Change logo", + "ACTION_USE_DEFAULT_LOGO": "Use default image" + }, + "REPORTS": { + "TITLE": "Rapporter", + "SUBTITLE": "Exportera dina projektdata i CSV-format och skapa dina egna rapporter. ", + "DESCRIPTION": "Hämta en CSV-fil eller kopiera den genererade länken och öppna i din favorit ordbehandlare eller kalkylark för att skapa dina egna projektdatarapporter. Du kan lätt visualisera och analysera alla dina data. ", + "HELP": "Hur använda i mitt eget kalkylark?", + "REGENERATE_TITLE": "Ändra länken", + "REGENERATE_SUBTITLE": "Du kan ändra CSV för datalänken. Den tidigare länken tas bort. Är du säker på det? " + }, + "CSV": { + "SECTION_TITLE_US": "rapporter för användarhistorier", + "SECTION_TITLE_TASK": "Rapport för uppgifter", + "SECTION_TITLE_ISSUE": "Rapporter för ärenden", + "DOWNLOAD": "Hämta CSV", + "URL_FIELD_PLACEHOLDER": "Vänligen skapa en ny CSV-länk", + "TITLE_REGENERATE_URL": "Skapa CSV länk", + "ACTION_GENERATE_URL": "Skapa länk", + "ACTION_REGENERATE": "Regenerera" + }, + "CUSTOM_FIELDS": { + "TITLE": "Anpassade fält", + "SUBTITLE": "Specificera anpassade fält för användarhistorier, uppgifter och ärenden. ", + "US_DESCRIPTION": "Användarhistorier för anpassade fält", + "US_ADD": "Lägg till ett anpassad fält i användarhistorien", + "TASK_DESCRIPTION": "Anpassade fält för uppgifter", + "TASK_ADD": "Lägg till anpassade fält i uppgifter", + "ISSUE_DESCRIPTION": "Anpassade fält för ärenden", + "ISSUE_ADD": "Anpassade fält för ärenden", + "FIELD_TYPE_TEXT": "Text", + "FIELD_TYPE_MULTI": "Flerradig", + "FIELD_TYPE_DATE": "Datum" + }, + "PROJECT_VALUES": { + "PAGE_TITLE": "{{sectionName}} - Projektvärden - {{projectName}}", + "REPLACEMENT": "Alla poster med det här värdet vill ändras till ", + "ERROR_DELETE_ALL": "Du kan inte ta bort alla värden. " + }, + "PROJECT_VALUES_POINTS": { + "TITLE": "Poäng", + "SUBTITLE": "Specificera antal poäng som användarhistorien ska estimeras till", + "US_TITLE": "US-poäng", + "ACTION_ADD": "Lägg till nytt punkt" + }, + "PROJECT_VALUES_PRIORITIES": { + "TITLE": "Prioritet", + "SUBTITLE": "Specificera den prioriteten dina ärenden ska ha", + "ISSUE_TITLE": "Prioritet för ärenden ", + "ACTION_ADD": "Lägg till en ny prioritet" + }, + "PROJECT_VALUES_SEVERITIES": { + "TITLE": "Allvarsgrad", + "SUBTITLE": "Specificera den allvarsgrad dina ärenden vill ha", + "ISSUE_TITLE": "Allvarighetsgrad för ärenden", + "ACTION_ADD": "Lägg till ny allvarlighetsgrad" + }, + "PROJECT_VALUES_STATUS": { + "TITLE": "Status", + "SUBTITLE": "Specificera status för dina användarhistorier, uppgifter och ärenden ska ha i olika faser. ", + "US_TITLE": "US statuser", + "TASK_TITLE": "Status för uppgifter", + "ISSUE_TITLE": "Status för ärenden" + }, + "PROJECT_VALUES_TYPES": { + "TITLE": "Typ", + "SUBTITLE": "Specificera typer av ärenden", + "ISSUE_TITLE": "Ärendetyper", + "ACTION_ADD": "Lägg till ny {{objName}}" + }, + "ROLES": { + "PAGE_TITLE": "Roller - {{projectName}}", + "WARNING_NO_ROLE": "Var försiktig. Inga roller i ditt projekt kan estimera poängvärden för användarhistorier", + "HELP_ROLE_ENABLED": "När det är aktivt, vill medlemmarna tilldelad den här rollen kunna estimera punktvärlden för användarhistorier", + "DISABLE_COMPUTABLE_ALERT_TITLE": "Avaktivera beräkningar för den här rollen", + "DISABLE_COMPUTABLE_ALERT_SUBTITLE": "Om du avaktiverar behörigheter för beräkning för rollen {{roleName}} vill alla tidigare beräkningar gjord av den här rollen tas bort. ", + "DISABLE_COMPUTABLE_ALERT_MESSAGE": "ÄR du säker på att du vill avvaktivera den här rollens beräkningar?", + "COUNT_MEMBERS": "{{ role.members_count }} medlemmar med den här rollen", + "TITLE_DELETE_ROLE": "Ta bort rollen", + "REPLACEMENT_ROLE": "Alla användarna med den här rollen vill flyttas till", + "WARNING_DELETE_ROLE": "Var försiktig. Alla beräkninger för den här rollen vill tas bort", + "ERROR_DELETE_ALL": "Du kan inte ta bort alla värden", + "EXTERNAL_USER": "Extern användare" + }, + "THIRD_PARTIES": { + "SECRET_KEY": "Hemlig nyckel", + "PAYLOAD_URL": "Länk för paketinnhåll", + "VALID_IPS": "Giltig ursprunglig IP-adresser (separerat med ,)" + }, + "BITBUCKET": { + "SECTION_NAME": "Bitbucket", + "PAGE_TITLE": "Bitbucket - {{projectName}}", + "INFO_VERIFYING_IP": "Bitbucket-förfrågningar är inte signerad så det bästa är att bekräfta ursprunglig IP. Om fältet är tomt blir det ingen IP-kontroll. " + }, + "GITLAB": { + "SECTION_NAME": "Gitlab", + "PAGE_TITLE": "Gitlab - {{projectName}}", + "INFO_VERIFYING_IP": "Gitlab-förfrågningar är inte signerade. Det bästa är att kontrollera ursprunglig IP-adress. Om fältet är tomt vill det inte bli någon IP-kontroll." + }, + "GITHUB": { + "SECTION_NAME": "Github", + "PAGE_TITLE": "Github - {{projectName}}" + }, + "WEBHOOKS": { + "PAGE_TITLE": "Webbkrok - {{projectName}}", + "SECTION_NAME": "Webbkrokar", + "SUBTITLE": "Webbkrokar notifierar externa tjänster om händelser i Taiga, till exempel kommentarer, användarhistorier ...", + "ADD_NEW": "Lägg till en ny Webbkrok", + "TYPE_NAME": "Skriv in tjänstens namn", + "TYPE_PAYLOAD_URL": "Skriv in länken till tjänsten", + "TYPE_SERVICE_SECRET": "Skriv in tjänstens hemliga nyckel", + "SAVE": "Spara webbkroken", + "CANCEL": "Avbryt webbkrok", + "SHOW_HISTORY": "(Vis historiken)", + "TEST": "Testa webbkroken", + "EDIT": "Ändra webkrok", + "DELETE": "Ta bort webbkrok", + "REQUEST": "Förfrågan", + "RESEND_REQUEST": "Skicka på nytt förfrågan", + "HEADERS": "Rubriker", + "PAYLOAD": "Tjänster", + "RESPONSE": "Respons", + "DATE": "DD MM YYY [at} hh:mm:ss", + "ACTION_HIDE_HISTORY": "(Dölj historiken)", + "ACTION_HIDE_HISTORY_TITLE": "Dölj detaljer för historiken", + "ACTION_SHOW_HISTORY": "(Vis historiken)", + "ACTION_SHOW_HISTORY_TITLE": "Visa detaljer för historiken", + "WEBHOOK_NAME": "Webbkrok '{{name}}'" + }, + "CUSTOM_ATTRIBUTES": { + "PAGE_TITLE": "{{sectionName}} - Anpassade egenskaper - {{projectName}}", + "ADD": "Lägg till ett anpassad fält", + "EDIT": "Ändra anpassad fält", + "DELETE": "Ta bort anpassad fält", + "SAVE_TITLE": "Spara anpassad fält", + "CANCEL_TITLE": "Avbryt det du skapar", + "SET_FIELD_NAME": "Sätt ditt anpassade fältnamn", + "SET_FIELD_DESCRIPTION": "Sätt beskrivning för anpassad fält", + "FIELD_TYPE_DEFAULT": "-- välj en --", + "ACTION_UPDATE": "Uppdatera anpassad fält", + "ACTION_CANCEL_EDITION": "Avbryt ändringen" + }, + "MEMBERSHIP": { + "COLUMN_MEMBER": "Medlem", + "COLUMN_ADMIN": "Administrator", + "COLUMN_ROLE": "Roll", + "COLUMN_STATUS": "Status", + "STATUS_ACTIVE": "Aktiv", + "STATUS_PENDING": "Avvaktar", + "DELETE_MEMBER": "Ta bort medlem", + "RESEND": "Skicka på nytt", + "SUCCESS_SEND_INVITATION": "Vi har skickat på nytt invitationen till '{{email}}'.", + "ERROR_SEND_INVITATION": "Vi har inte skickat invitationen", + "SUCCESS_DELETE": "Vi har tagit bort {{message}}.", + "ERROR_DELETE": "Vi har inte lyckats att ta bort {{message}}.", + "DEFAULT_DELETE_MESSAGE": "den här invitationen till {{email}}" + }, + "DEFAULT_VALUES": { + "LABEL_POINTS": "Standardvärde för poängväljaren", + "LABEL_US": "Standardvärde för US-statusväljare", + "LABEL_TASK_STATUS": "Standardvärdet för val av uppgiftsstatus", + "LABEL_PRIORITY": "Standardvärde för val av prioritet", + "LABEL_SEVERITY": "Standardvärde för val av allvarlighet", + "LABEL_ISSUE_TYPE": "Standardvärde för ärendetyp-väljare", + "LABEL_ISSUE_STATUS": "Standardvärde för väljare för ärendestatus" + }, + "STATUS": { + "PLACEHOLDER_WRITE_STATUS_NAME": "Skriv ett namn för den nya statusen" + }, + "TYPES": { + "PLACEHOLDER_WRITE_NAME": "Skriv ett namn för det nya elementet" + }, + "US_STATUS": { + "ACTION_ADD_STATUS": "Lägg till ny status", + "IS_ARCHIVED_COLUMN": "Är det arkiverad?", + "WIP_LIMIT_COLUMN": "WIP-begränsning", + "PLACEHOLDER_WRITE_NAME": "Skriv ett namn för den nya statusen" + }, + "MENU": { + "TITLE": "Administrator", + "PROJECT": "Projekt", + "ATTRIBUTES": "Egenskaper", + "MEMBERS": "Medlemmar", + "PERMISSIONS": "Behörigheter", + "INTEGRATIONS": "Integrationer", + "PLUGINS": "Insticksprogram" + }, + "SUBMENU_PROJECT_ATTRIBUTES": { + "TITLE": "Egenskaper" + }, + "SUBMENU_PROJECT_VALUES": { + "STATUS": "Status", + "POINTS": "Poäng", + "PRIORITIES": "Prioritet", + "SEVERITIES": "Allvarsgrad", + "TYPES": "Typ", + "CUSTOM_FIELDS": "Anpassade fält" + }, + "SUBMENU_PROJECT_PROFILE": { + "TITLE": "Projektprofil" + }, + "SUBMENU_ROLES": { + "TITLE": "Roller", + "ACTION_NEW_ROLE": "+ Ny roll", + "TITLE_ACTION_NEW_ROLE": "Lägg till ny roll" + }, + "SUBMENU_THIDPARTIES": { + "TITLE": "Tjänster" + } + }, + "USER": { + "PROFILE": { + "PAGE_TITLE": "{{userFullName}} (@{{userUsername}})", + "EDIT": "Ändra profil", + "FOLLOW": "Följ", + "CLOSED_US": "Stängd US", + "PROJECTS": "Projekt", + "PROJECTS_EMPTY": "{{username}} har inga projekt än", + "CONTACTS": "Kontakter", + "CONTACTS_EMPTY": "{{username}} har inte några kontakter än", + "CURRENT_USER_CONTACTS_EMPTY": "Du har inga kontakter än", + "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "De du arbetar med på Taiga vill automatiskt bli dina kontakter. ", + "REPORT": "Rapportera misbruk", + "TABS": { + "ACTIVITY_TAB": "Tidslinje", + "ACTIVITY_TAB_TITLE": "Visa alla aktiviteter för den här användaren", + "PROJECTS_TAB": "Projekt", + "PROJECTS_TAB_TITLE": "Lista på alla projekt som användaren är medlem i", + "LIKES_TAB": "Gillar", + "LIKES_TAB_TITLE": "Lista allt vad användaren gillar", + "VOTES_TAB": "Röster", + "VOTES_TAB_TITLE": "Lista alla röster från den här användaren", + "WATCHED_TAB": "Visad", + "WATCHED_TAB_TITLE": "Lista alla element som användaren ser", + "CONTACTS_TAB": "Kontakter", + "CONTACTS_TAB_TITLE": "Lista alla kontakter för den här användaren" + } + }, + "PROFILE_SIDEBAR": { + "TITLE": "Din profil", + "DESCRIPTION": "Folk kan se allt vad du gör och vad du arbetar med. Lägg till en trevlig bio för att förhöja din information. ", + "ADD_INFO": "Ändra bio" + }, + "PROFILE_FAVS": { + "FILTER_INPUT_PLACEHOLDER": "Skriv något ...", + "FILTER_TYPE_ALL": "Alla", + "FILTER_TYPE_ALL_TITLE": "Visa alla", + "FILTER_TYPE_PROJECTS": "Projekt", + "FILTER_TYPE_PROJECT_TITLES": "Visa bara projekt", + "FILTER_TYPE_USER_STORIES": "Berättelser", + "FILTER_TYPE_USER_STORIES_TITLES": "Visa endast användarhistorier", + "FILTER_TYPE_TASKS": "Uppgift", + "FILTER_TYPE_TASK_TITLES": "Visar endast uppgifter", + "FILTER_TYPE_ISSUES": "Ärenden", + "FILTER_TYPE_ISSUES_TITLE": "Visa bara ärenden ", + "EMPTY_TITLE": "Det verkar som om vi inte har något att visa just nu" + } + }, + "PROJECT": { + "PAGE_TITLE": "{{projectName}}", + "WELCOME": "Välkommen", + "SECTION_PROJECTS": "Projekt", + "HELP": "Organisera dina projekt och sätt in de mest använda här.
De första 10 toppprojekten vill visas i toppnavigeringens projektlista. ", + "PRIVATE": "Privata projekt", + "LOOKING_FOR_PEOPLE": "This project is looking for people", + "FANS_COUNTER_TITLE": "{total, plural, one{one fan} andra{# fans}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, en{one watcher} andra{# watchers}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", + "STATS": { + "PROJECT": "projekt
poäng", + "DEFINED": "definierad
poäng", + "ASSIGNED": "tilldelad
poäng", + "CLOSED": "stängd
poäng" + }, + "SECTION": { + "SEARCH": "Sök", + "TIMELINE": "Tidslinje", + "BACKLOG": "Inkorg", + "KANBAN": "Kanban", + "ISSUES": "Ärenden", + "WIKI": "Wiki", + "TEAM": "Arbetsgrupp", + "MEETUP": "Möt upp", + "ADMIN": "Administrator" + }, + "NAVIGATION": { + "SECTION_TITLE": "Dina projekt", + "PLACEHOLDER_SEARCH": "Sök i ...", + "ACTION_CREATE_PROJECT": "Skapa projekt", + "ACTION_IMPORT_PROJECT": "Importerar projekt", + "MANAGE_PROJECTS": "Manage projects", + "TITLE_CREATE_PROJECT": "Skapa projekt", + "TITLE_IMPORT_PROJECT": "Importerar projekt", + "TITLE_PRVIOUS_PROJECT": "Visa tidigare projekt", + "TITLE_NEXT_PROJECT": "Visa nästa projekt", + "HELP_TITLE": "Taiga hjälpsida", + "HELP": "Hjälp", + "HOMEPAGE": "Homepage", + "FEEDBACK_TITLE": "Skicka återkoppling", + "FEEDBACK": "Återkokppling", + "NOTIFICATIONS_TITLE": "Ändra inställningar för dina notifieringar", + "NOTIFICATIONS": "Notifieringar", + "ORGANIZATIONS_TITLE": "Ändra dina organisationer", + "ORGANIZATIONS": "Ändra organizationer", + "SETTINGS_TITLE": "Ändra inställningarna", + "SETTINGS": "Inställningar", + "VIEW_PROFILE_TITLE": "Vis profil", + "VIEW_PROFILE": "Vis profil", + "EDIT_PROFILE_TITLE": "Ändra din profil", + "EDIT_PROFILE": "Ändra profil", + "CHANGE_PASSWORD_TITLE": "Byt lösenord", + "CHANGE_PASSWORD": "Byt lösenord", + "DASHBOARD_TITLE": "Instrumentpanel", + "DISCOVER_TITLE": "Upptäck nya rörliga projekt", + "DISCOVER": "Upptäck", + "ACTION_REORDER": "Dra & släpp för att sortera" + }, + "IMPORT": { + "TITLE": "Importerar projekt", + "UPLOADING_FILE": "Ladda hämtningsfilen", + "DESCRIPTION": "Den här processen kan ta en liten stund, vänligen försök med fönstret öppet. ", + "ASYNC_IN_PROGRESS_TITLE": "Våra Oompa Loompier importerar ditt projekt", + "ASYNC_IN_PROGRESS_MESSAGE": "Den här processen kan ta några minuter
. Vi vill skicka dig en e-post när det blir klart. ", + "UPLOAD_IN_PROGRESS_MESSAGE": "Laddat upp {{uploadedSize}} av {{totalSize}}", + "ERROR": "Våra Oompa Loompier har problem med att importera din hämtningsfil. Vänligen försök igen. ", + "ERROR_TOO_MANY_REQUEST": "Beklagar. Våra oompa loompier är mycket upptagna. Vänligen försök på nytt om några minuter. ", + "ERROR_MESSAGE": "Våra Oompa Loompier har lite problem med att importera dina hämtningsdata: {{error_message}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) är för tungt för våra Oompa Loompier. Försök med något mindre än ({{maxFileSize}})", + "SYNC_SUCCESS": "Ditt projekt är korrekt importerad" + }, + "LIKE_BUTTON": { + "LIKE": "Gillar", + "LIKED": "Likte", + "UNLIKE": "Ogillar", + "BUTTON_TITLE": "Gilla eller ogilla projektet", + "COUNTER_TITLE": "{total, plural, one{one fan} andra{# fans}}" + }, + "WATCH_BUTTON": { + "BUTTON_TITLE": "Visa projektet och lägg till egenskaper för avisering", + "WATCH": "Visa", + "WATCHING": "Bevakar", + "COUNTER_TITLE": "{total, plural, en{one watcher} andra{# watchers}}", + "OPTIONS": { + "NOTIFY_ALL": "Motta alla notifieringar", + "NOTIFY_ALL_TITLE": "Motta alla notifieringar för det här projektet", + "NOTIFY_INVOLVED": "Endast involverad", + "NOTIFY_INVOLVED_TITLE": "Motta notifieringar bara när du är involverad", + "UNWATCH": "Frånkoppla visning", + "UNWATCH_TITLE": "Visa inte projektet" + } + } + }, + "LIGHTBOX": { + "DELETE_ACCOUNT": { + "SECTION_NAME": "Ta bort konto", + "CONFIRM": "Är du säker på att du vill radera kontot?", + "SUBTITLE": "Vi kommer att sakna dig!", + "NEWSLETTER_LABEL_TEXT": "Jag vill inte motta flera nyhetsbrev" + }, + "DELETE_PROJECT": { + "TITLE": "Ta bort projekt", + "QUESTION": "Är du säker på att du vill ta bort projektet?", + "SUBTITLE": "Alla projektdata (användarhistorier, uppgifter, ärenden, sprintar och wikisidor) vill raderas! :-(", + "CONFIRM": "Ja! Jag är verkligen säker på det" + }, + "ASSIGNED_TO": { + "SELECT": "Välj att tilldela till", + "SEARCH": "Sök efter användare" + }, + "ADD_MEMBER": { + "TITLE": "Ny medlem", + "HELP_TEXT": "Om användaren redan är registrerad på Taiga vill hen bli lagt till automatiskt. I annat fall vill de motta en invitation. " + }, + "CREATE_ISSUE": { + "TITLE": "Lägg till ärende" + }, + "FEEDBACK": { + "TITLE": "Berätta för oss ...", + "COMMENT": "... en bug, några förslag, något spännande ... eller din värsta mardröm med Taiga", + "ACTION_SEND": "Skicka återkoppling" + }, + "SEARCH": { + "TITLE": "Sök", + "PLACEHOLDER_SEARCH": "Vad letar du efter?" + }, + "ADD_EDIT_SPRINT": { + "TITLE": "Ny sprint", + "PLACEHOLDER_SPRINT_NAME": "sprintens namn", + "PLACEHOLDER_SPRINT_START": "Beräknad start", + "PLACEHOLDER_SPRINT_END": "Beräknad slutdato", + "ACTION_DELETE_SPRINT": "Vill du verkligen ta bort den här sprinten?", + "TITLE_ACTION_DELETE_SPRINT": "ta bort sprint", + "LAST_SPRINT_NAME": "senaste sprint är {{lastSprint}} ;-) " + }, + "CREATE_EDIT_TASK": { + "TITLE": "Ny uppgift", + "PLACEHOLDER_SUBJECT": "Lägg till titel", + "PLACEHOLDER_STATUS": "Status för uppgift", + "OPTION_UNASSIGNED": "Otilldelad", + "PLACEHOLDER_SHORT_DESCRIPTION": "Skriv en kort beskrivning", + "ACTION_EDIT": "Ändra uppgiften" + }, + "CREATE_EDIT_US": { + "TITLE": "Nytt US", + "PLACEHOLDER_DESCRIPTION": "Vänligen lägg till en beskrivande text för att hjälpa andra att förstå den här användarhistorien. ", + "NEW_US": "Ny användarhistorie", + "EDIT_US": "Ändra användarhistorien" + }, + "DELETE_SPRINT": { + "TITLE": "Ta bort sprint" + }, + "CREATE_MEMBER": { + "PLACEHOLDER_INVITATION_TEXT": "(Valfritt) Lägg till en personlig hälsning till invitationen. Berätta något trevligt till din nya projektmedlem ;-)", + "PLACEHOLDER_TYPE_EMAIL": "Skriv in en e-postadress" + } + }, + "US": { + "PAGE_TITLE": "{{userStorySubject}} - Användarhistorier {{userStoryRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Status: {{userStoryStatus }}. avslutad{{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} av {{userStoryTotalTasks}} tasks closed). Poäng: {{userStoryPoints}}. Beskrivning: {{userStoryDescription}}", + "SECTION_NAME": "Detaljer för användarhistorier", + "LINK_TASKBOARD": "Uppgiftstavla", + "TITLE_LINK_TASKBOARD": "Gå till uppgiftstavlan", + "TOTAL_POINTS": "totalpoäng", + "ADD": "+ Lägg till ny användarhistorie", + "ADD_BULK": "Lägg till flera nya användarhistorier", + "PROMOTED": "Denna US har blivit flyttat från ärende: ", + "TITLE_LINK_GO_TO_ISSUE": "Gå till ärende", + "EXTERNAL_REFERENCE": "Denna användarhistorien är skapat från", + "GO_TO_EXTERNAL_REFERENCE": "Gå till början", + "BLOCKED": "Användarhistorien är blockerad", + "PREVIOUS": "tidigare användarhistorie", + "NEXT": "nästa användarhistorie", + "TITLE_DELETE_ACTION": "Ta bort användarhistorien", + "LIGHTBOX_TITLE_BLOKING_US": "Blockera oss", + "TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} uppgifter kompletta", + "ASSIGN": "Lägg till användarhistorie", + "NOT_ESTIMATED": "Ej beräknad", + "TOTAL_US_POINTS": "Total US-poäng", + "FIELDS": { + "TEAM_REQUIREMENT": "Teamets behov", + "CLIENT_REQUIREMENT": "Kräver beställare", + "FINISH_DATE": "Färdig datum" + } + }, + "COMMENTS": { + "DELETED_INFO": "Kommentar raderad av {{user}} den {{date}}", + "TITLE": "Kommentarer", + "COMMENT": "Kommentarer", + "TYPE_NEW_COMMENT": "Skriv en ny kommentar här", + "SHOW_DELETED": "Visa raderade kommentarer", + "HIDE_DELETED": "Dölj raderade kommentarer", + "DELETE": "Delete comment", + "RESTORE": "Hämta tillbaka tidigare kommentarer" + }, + "ACTIVITY": { + "SHOW_ACTIVITY": "Visa aktiviteter", + "DATETIME": "YYYY-MM-DD HH:mm", + "SHOW_MORE": "+ Visa tidigare poster ({{showMore}} more)", + "TITLE": "Aktiviteter", + "REMOVED": "borttaget", + "ADDED": "lagt till", + "US_POINTS": "US-poäng ({{name}})", + "NEW_ATTACHMENT": "ny bilaga", + "DELETED_ATTACHMENT": "ta bort bifogad fil", + "UPDATED_ATTACHMENT": "uppdaterad bilaga {{filename}}", + "DELETED_CUSTOM_ATTRIBUTE": "raderad anpassad atribut", + "SIZE_CHANGE": "Gjorde {size, plural, one{one change} annan{# changes}}", + "VALUES": { + "YES": "ja", + "NO": "nej", + "EMPTY": "tom", + "UNASSIGNED": "otilldelad" + }, + "FIELDS": { + "SUBJECT": "titel", + "NAME": "namn", + "DESCRIPTION": "beskrivning", + "CONTENT": "innehåll", + "STATUS": "status", + "IS_CLOSED": "är stängd", + "FINISH_DATE": "färdig datum", + "TYPE": "typ", + "PRIORITY": "prioritet", + "SEVERITY": "Allvarsgrad", + "ASSIGNED_TO": "tilldelad till", + "WATCHERS": "bevakare", + "MILESTONE": "sprint", + "USER_STORY": "Användarhistorie", + "PROJECT": "projekt", + "IS_BLOCKED": "är blockerad", + "BLOCKED_NOTE": "blockerad notering", + "POINTS": "poäng", + "CLIENT_REQUIREMENT": "kräver beställare", + "TEAM_REQUIREMENT": "krav arbetsgrupp", + "IS_IOCAINE": "är Iocaine", + "TAGS": "etiketter", + "ATTACHMENTS": "bilagor", + "IS_DEPRECATED": "undviks", + "ORDER": "sortera", + "BACKLOG_ORDER": "sortera inkorgen", + "SPRINT_ORDER": "sortera sprintar", + "KANBAN_ORDER": "kanban-sortering", + "TASKBOARD_ORDER": "Sortera uppgiftstavlan", + "US_ORDER": "sortera US" + } + }, + "BACKLOG": { + "PAGE_TITLE": "Inkorg - {{projectName}}", + "PAGE_DESCRIPTION": "Panelen för inkorgen, användarhistorier och sprintar i projektet {{projectName}}: {{projectDescription}}", + "SECTION_NAME": "Inkorg", + "CUSTOMIZE_GRAPH": "Anpassa din graf för inkorgen", + "CUSTOMIZE_GRAPH_TEXT": "För att få en fin graf som hjälper dig att följa projektets gång kan du sätta upp poäng och sprintar genom ", + "CUSTOMIZE_GRAPH_ADMIN": "Administrator", + "CUSTOMIZE_GRAPH_TITLE": "Lägg till poäng och sprintar via Administratorn", + "MOVE_US_TO_CURRENT_SPRINT": "Flytta till nuvarande sprint", + "MOVE_US_TO_LATEST_SPRINT": "Move to latest Sprint", + "SHOW_FILTERS": "Visa filter", + "SHOW_TAGS": "Visa etiketter", + "EMPTY": "Inkorgen är tom!", + "CREATE_NEW_US": "Skapa en ny US", + "CREATE_NEW_US_EMPTY_HELP": "Du kanske vill skapa en ny användarhistorie", + "EXCESS_OF_POINTS": "Poängöverskott", + "PENDING_POINTS": "Poäng som väntar", + "CLOSED_POINTS": "stängd", + "COMPACT_SPRINT": "Komprimera sprint", + "GO_TO_TASKBOARD": "Gå till uppgiftstavlan till {{::name}}", + "EDIT_SPRINT": "Ändra sprint", + "TOTAL_POINTS": "totalt", + "STATUS_NAME": "Status namn", + "SORTABLE_FILTER_ERROR": "Du kan inte lägga till i inkorgen när filter är öppen", + "DOOMLINE": "Projektbegränsningar [Doomline]", + "CHART": { + "XAXIS_LABEL": "Sprintar", + "YAXIS_LABEL": "Poäng", + "OPTIMAL": "Optimala poäng som avvaktar sprinten \"{{sprintName}}\" skulle varit {{value}}", + "REAL": "Verkliga poäng som avvaktar för sprinten \"{{sprintName}}\" is {{value}}", + "INCREMENT_TEAM": "Ökande poäng för arbetsgruppens krav för sprint \"{{sprintName}}\" är {{value}}", + "INCREMENT_CLIENT": "Ökande poäng för beställarkrav till sprinten \"{{sprintName}}\" är {{value}}" + }, + "TAGS": { + "TOGGLE": "Växla visning av etiketter", + "SHOW": "Visa etiketter", + "HIDE": "Dölj etiketter" + }, + "TABLE": { + "COLUMN_US": "Användarhistorie", + "TITLE_COLUMN_POINTS": "Välj visning per roll" + }, + "SPRINT_SUMMARY": { + "TOTAL_POINTS": "totalt
poäng", + "COMPLETED_POINTS": "avslutad
poäng", + "OPEN_TASKS": "öppna
uppgifter\n", + "CLOSED_TASKS": "stängd
uppgifer", + "IOCAINE_DOSES": "iocaine
doser", + "SHOW_STATISTICS_TITLE": "Visa statistik", + "TOGGLE_BAKLOG_GRAPH": "Visa/Dölj burn down-graf" + }, + "SUMMARY": { + "PROJECT_POINTS": "projekt
poäng", + "DEFINED_POINTS": "definiera
poäng", + "CLOSED_POINTS": "stängda
poäng", + "POINTS_PER_SPRINT": "poäng/
sprint" + }, + "FILTERS": { + "TOGGLE": "Växla filtrens synlighet", + "TITLE": "Filter", + "REMOVE": "Ta bort filter", + "HIDE": "Dölj filter", + "SHOW": "Visa filter", + "FILTER_CATEGORY_STATUS": "Status", + "FILTER_CATEGORY_TAGS": "Etiketter" + }, + "SPRINTS": { + "TITLE": "SPRINTAR", + "DATE": "YYYY-MM-DD", + "LINK_TASKBOARD": "Sprint uppgiftspanel", + "TITLE_LINK_TASKBOARD": "Gå till uppgiftspanelen för \"{{name}}\"", + "NUMBER_SPRINTS": "
sprintar\n", + "EMPTY": "Det är inga sprintar än", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "This sprint has no User Stories", + "WARNING_EMPTY_SPRINT": "Här kan du föra in användarhistorier från inkorgen för att starta en ny sprint", + "TITLE_ACTION_NEW_SPRINT": "Lägg till ny sprint", + "TEXT_ACTION_NEW_SPRINT": "Du kanske vill skapa en ny sprint i ditt projekt", + "ACTION_SHOW_CLOSED_SPRINTS": "Visa stängda sprintar", + "ACTION_HIDE_CLOSED_SPRINTS": "Dölj stängda sprintar" + } + }, + "ERROR": { + "TEXT1": "Någonting har hänt och våra Oompa Loompier arbetar på att hitta en lösning. ", + "NOT_FOUND": "Hittade inte", + "NOT_FOUND_TEXT": "Fel 404. Sidan du letar efter existerar inte längre. Du kan gå tillbaka till TAIGA hemsidan och se om du hittar vad du letar efter. ", + "PERMISSION_DENIED": "Du har inte behöriget", + "PERMISSION_DENIED_TEXT": "Du har inte behörighet till den här sidan. ", + "VERSION_ERROR": "Någon inuti Taiga har ändrat den här tidigare och våra Oompa Loompier kan inte lägga till din ändring. Vänligen hämta sidan på nytt och lägg till ändringarna (de här ändringar du gör just nu förlorar du). " + }, + "TASKBOARD": { + "PAGE_TITLE": "{{sprintName}} - Sprint uppgiftstavla - {{projectName}}", + "PAGE_DESCRIPTION": "Sprint {{sprintName}} (from {{startDate}} till {{endDate}}) av {{projectName}}. Avslutad {{completedPercentage}} % ({{completedPoints}} av {{totalPoints}} points). {{openTasks}} öppna ärenden av {{totalTasks}}.", + "SECTION_NAME": "Uppgiftstavla", + "TITLE_ACTION_ADD": "Lägg till en ny uppgift", + "TITLE_ACTION_ADD_BULK": "Lägg till några flera nya uppgifter", + "TITLE_ACTION_ASSIGN": "Tilldela uppgift", + "TITLE_ACTION_EDIT": "Ändra uppgiften", + "PLACEHOLDER_CARD_TITLE": "Det har kan vara en uppgift", + "PLACEHOLDER_CARD_TEXT": "Dela användarhistorier upp i uppgifter och spåra dem separat", + "TABLE": { + "COLUMN": "Användarhistorie", + "TITLE_ACTION_FOLD": "Vika i hop kolumnen", + "TITLE_ACTION_UNFOLD": "Veckla ut kolumnen", + "TITLE_ACTION_FOLD_ROW": "Vika i hop raden", + "TITLE_ACTION_UNFOLD_ROW": "Veckla ut rad", + "FIELD_POINTS": "poäng", + "ROW_UNASSIGED_TASKS_TITLE": "Ej tilldelade uppgifter" + }, + "CHARTS": { + "XAXIS_LABEL": "Dagar", + "YAXIS_LABEL": "Poäng", + "OPTIMAL": "Optimala poäng som väntar för dag {{formattedDate}} ska vara {{roundedValue}}", + "REAL": "Verkliga poäng som väntar för dag {{formattedDate}} är {{roundedValue}}", + "DATE": "DD MM YYYY" + } + }, + "TASK": { + "PAGE_TITLE": "{{taskSubject}} - Uppgift {{taskRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Status: {{taskStatus }}. Beskrivning: {{taskDescription}}", + "SECTION_NAME": "Detaljer för uppgiften", + "LINK_TASKBOARD": "Uppgiftstavla", + "TITLE_LINK_TASKBOARD": "Gå till uppgiftstavlan", + "PLACEHOLDER_SUBJECT": "Skriv in den nya uppgiftens titel", + "TITLE_SELECT_STATUS": "Status namn", + "OWNER_US": "Den här uppgiften tillhör", + "TITLE_LINK_GO_OWNER": "Gå till användarhistorie", + "ORIGIN_US": "Den här uppgiften är skapad från", + "TITLE_LINK_GO_ORIGIN": "Gå till användarhistorie", + "BLOCKED": "Uppgiften är blockerad", + "PREVIOUS": "tidigare uppgift", + "NEXT": "ny uppgift", + "TITLE_DELETE_ACTION": "Ta bort uppgift", + "LIGHTBOX_TITLE_BLOKING_TASK": "Blockerad uppgift", + "FIELDS": { + "MILESTONE": "Sprint", + "USER_STORY": "Användarhistorie", + "IS_IOCAINE": "Är iocaine" + }, + "ACTION_IOCAINE": "Iocaine", + "TITLE_ACTION_IOCAINE": "Känner du dig lite bortkommen med en uppgift? Försäkra dig om att andra känner till uppgiften när du klickar på Iocaine-knappen när du ändrar uppgiften. Det är möjligt att bli immun till det här (påhittade) dödliga giftet om du tar små mängder över tid - och du kan även så småningom om bli bättre på vad du gör när du då och då tar på dig större utmaningar!" + }, + "NOTIFICATION": { + "OK": "Allt är OK", + "WARNING": "Ojsan, något har hänt ...", + "WARNING_TEXT": "Våra Oompa Loompier är ledsna, dina ändringar gick inte att spara!", + "SAVED": "Vår oompa loompier har sparat alla dina ändringar!", + "CLOSE": "Stäng avisering", + "MAIL": "Notifieringar via e-post", + "ASK_DELETE": "Är du säker på att du vill radera?" + }, + "CANCEL_ACCOUNT": { + "TITLE": "Ta bort ditt konto", + "SUBTITLE": "Vi beklagar att du lämnar Taiga, men hoppas att du hade det trevligt i alla fall :-)", + "PLACEHOLDER_INPUT_TOKEN": "avbryt kontoförekomsten", + "ACTION_LEAVING": "Ja. Jag lämnar det!", + "SUCCESS": "Våra Oompa Loompier har tagit bort ditt konto" + }, + "CHANGE_EMAIL_FORM": { + "TITLE": "Ändra din e-postadress", + "SUBTITLE": "Ett klick mera och din e-post uppdateras!", + "PLACEHOLDER_INPUT_TOKEN": "ändra e-postförekomsten", + "ACTION_CHANGE_EMAIL": "Ändra din e-postadress", + "SUCCESS": "Våra Oompa Loompier uppdaterade din e-postadress" + }, + "ISSUES": { + "PAGE_TITLE": "Ärenden - {{projectName}}", + "PAGE_DESCRIPTION": "Ärenden som listas för projektet {{projectName}}: {{projectDescription}}", + "LIST_SECTION_NAME": "Ärenden", + "SECTION_NAME": "Detaljer för ärenden ", + "ACTION_NEW_ISSUE": "+ NYTT ÄRENDE", + "ACTION_PROMOTE_TO_US": "Flytta till användarhistorie", + "PLACEHOLDER_FILTER_NAME": "Skriv filternamnet och tryck på ", + "PROMOTED": "Ärendet har flyttats till US:", + "EXTERNAL_REFERENCE": "Den här uppgiften är skapat från", + "GO_TO_EXTERNAL_REFERENCE": "Gå till början", + "BLOCKED": "Det här ärendet är blockerad", + "TITLE_PREVIOUS_ISSUE": "tidigare ärende", + "TITLE_NEXT_ISSUE": "nästa ärende", + "ACTION_DELETE": "Ta bort ärende", + "LIGHTBOX_TITLE_BLOKING_ISSUE": "Blockerad ärende", + "FIELDS": { + "PRIORITY": "Prioritet", + "SEVERITY": "Allvarsgrad", + "TYPE": "Typ" + }, + "CONFIRM_PROMOTE": { + "TITLE": "Flytta det här ärendet till en ny användarhistorie", + "MESSAGE": "Är du säker på att du vill skapa en ny US från det här ärendet?" + }, + "FILTERS": { + "TITLE": "Filter", + "INPUT_SEARCH_PLACEHOLDER": "Titel eller referens", + "TITLE_ACTION_SEARCH": "Sök", + "ACTION_SAVE_CUSTOM_FILTER": "spara som anpassad filter", + "BREADCRUMB": "Filter", + "TITLE_BREADCRUMB": "Filter", + "CATEGORIES": { + "TYPE": "Typ", + "STATUS": "Status", + "SEVERITY": "Allvarsgrad", + "PRIORITIES": "Prioritet", + "TAGS": "Etiketter", + "ASSIGNED_TO": "Tilldelad till", + "CREATED_BY": "Skapad av", + "CUSTOM_FILTERS": "Anpassad filter" + }, + "CONFIRM_DELETE": { + "TITLE": "Ta bort anpassad filter.", + "MESSAGE": "anpassad filter '{{customFilterName}}'" + } + }, + "TABLE": { + "COLUMNS": { + "TYPE": "Typ", + "SEVERITY": "Allvarsgrad", + "PRIORITY": "Prioritet", + "SUBJECT": "Titel", + "VOTES": "Röster", + "STATUS": "Status", + "CREATED": "Skapad", + "ASSIGNED_TO": "Tilldelad till" + }, + "TITLE_ACTION_CHANGE_STATUS": "Ändra status", + "TITLE_ACTION_ASSIGNED_TO": "Tilldelad till", + "BLOCKED": "Blockerad", + "EMPTY": { + "TITLE": "Det är inte några ärenden att rapportera", + "SUBTITLE": "Hittade du ett ärende?" + } + } + }, + "ISSUE": { + "PAGE_TITLE": "{{issueSubject}} - Ärende {{issueRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Status: {{issueStatus }}. Typ: {{issueType}}, Prioritet: {{issuePriority}}. Allvarlighetsgrad: {{issueSeverity}}. Beskrivning: {{issueDescription}}" + }, + "KANBAN": { + "PAGE_TITLE": "Kanban - {{projectName}}", + "PAGE_DESCRIPTION": "Kanbanpanelet, med projekt {{projectName}}: {{projectDescription}} användarhistorier. ", + "SECTION_NAME": "Kanban", + "TITLE_ACTION_FOLD": "Vika i hop kolumnen", + "TITLE_ACTION_UNFOLD": "Veckla ut kolumnen", + "TITLE_ACTION_FOLD_CARDS": "Vika ut kortet", + "TITLE_ACTION_UNFOLD_CARDS": "Vika ut korten", + "TITLE_ACTION_ADD_US": "Lägg till ny användarhistorie", + "TITLE_ACTION_ADD_BULK": "Lägg till flera nya", + "ACTION_SHOW_ARCHIVED": "Visa arkiverad", + "ACTION_HIDE_ARCHIVED": "Dölj arkiverad", + "HIDDEN_USER_STORIES": "Användarhistorien för den här statusen är dolt som standard", + "ARCHIVED": "Du har arkiverad", + "UNDO_ARCHIVED": "Dra & släpp på nytt för att göra om", + "PLACEHOLDER_CARD_TITLE": "Detta är dina användarhistorier", + "PLACEHOLDER_CARD_TEXT": "Användarhistorier behöver även underuppgifter för att separera kraven." + }, + "SEARCH": { + "PAGE_TITLE": "Sök - {{projectName}}", + "PAGE_DESCRIPTION": "Sök på vad som helst, användarhistorier, uppgifter, ärenden och wiki-innehåll i projektet {{projectName}}: {{projectDescription}}", + "FILTER_USER_STORIES": "Användarhistorier", + "FILTER_ISSUES": "Ärenden", + "FILTER_TASKS": "Uppgift", + "FILTER_WIKI": "Wiki-sidor", + "PLACEHOLDER_SEARCH": "Sök i ...", + "TITLE_ACTION_SEARCH": "sök", + "EMPTY_TITLE": "Det ser ut som om vi inte hittade något för ditt sök. ", + "EMPTY_DESCRIPTION": "Kanske du kan försöka en av flikarna ovan eller sök på nytt" + }, + "TEAM": { + "PAGE_TITLE": "Team - {{projectName}}", + "PAGE_DESCRIPTION": "Panelen för arbetsgrupper visar alla medlemmar i projektet {{projectName}}: {{projectDescription}}", + "SECTION_NAME": "Arbetsgrupp", + "APP_TITLE": "Arbetsgrupp - {{projectName}}", + "PLACEHOLDER_INPUT_SEARCH": "Sök på hela namnet ...", + "COLUMN_MR_WOLF": "Herr Hansson", + "EXPLANATION_COLUMN_MR_WOLF": "Stängda ärenden", + "COLUMN_IOCAINE": "Iocaine-drinkare", + "EXPLANATION_COLUMN_IOCAINE": "Tagna iocaine-doser", + "COLUMN_CERVANTES": "Cervantes", + "EXPLANATION_COLUMN_CERVANTES": "Ändrade wiki-sidor", + "COLUMN_BUG_HUNTER": "Buggjägare", + "EXPLANATION_COLUMN_BUG_HUNTER": "Rapporterade ärenden", + "COLUMN_NIGHT_SHIFT": "Nattarbete", + "EXPLANATION_COLUMN_NIGHT_SHIFT": "Stängda uppgifter", + "COLUMN_TOTAL_POWER": "Totalkraft", + "EXPLANATION_COLUMN_TOTAL_POWER": "Totalpoäng", + "SECTION_TITLE_TEAM": "Arbetsgrupp >", + "SECTION_FILTER_ALL": "Alla", + "CONFIRM_LEAVE_PROJECT": "Är du säker på att du vill lämna projektet?", + "ACTION_LEAVE_PROJECT": "Lämna det här projektet" + }, + "USER_SETTINGS": { + "AVATAR_MAX_SIZE": "[Max. size: {{maxFileSize}}]", + "MENU": { + "SECTION_TITLE": "Inställningar för användare", + "USER_PROFILE": "Användarprofil", + "CHANGE_PASSWORD": "Byt lösenord", + "EMAIL_NOTIFICATIONS": "E-postnotifieringar" + }, + "NOTIFICATIONS": { + "SECTION_NAME": "E-postnotifieringar", + "COLUMN_PROJECT": "Projekt", + "COLUMN_RECEIVE_ALL": "Motta alla", + "COLUMN_ONLY_INVOLVED": "Bara involverade", + "COLUMN_NO_NOTIFICATIONS": "Inga notifieringar", + "OPTION_ALL": "Alla", + "OPTION_INVOLVED": "Involverad", + "OPTION_NONE": "Ingen" + }, + "POPOVER": { + "USER_PROFILE": "Användarprofil", + "CHANGE_PASSWORD": "Ändra lösenord", + "NOTIFICATIONS": "Notifieringar", + "FEEDBACK": "Återkokppling", + "TITLE_AVATAR": "Inställningar för användare" + } + }, + "USER_PROFILE": { + "IMAGE_HELP": "The image will be scaled to 80x80px.", + "ACTION_CHANGE_IMAGE": "Ändra", + "ACTION_USE_GRAVATAR": "Use default image", + "ACTION_DELETE_ACCOUNT": "Ta bort Taiga-kontot", + "CHANGE_EMAIL_SUCCESS": "Kontrollera din inbox!
Vi har skickad dig ett e-postmeddelande
med instruktioner för hur du sätter upp din nya adress", + "CHANGE_PHOTO": "Ändra foto", + "FIELD": { + "USERNAME": "Användarnamn", + "EMAIL": "E-post", + "FULL_NAME": "Hela namnet", + "PLACEHOLDER_FULL_NAME": "Skriv in hela namnet (ex. Medelsvensson)", + "BIO": "Personlig bio (max. 210 tecken)", + "PLACEHOLDER_BIO": "Berätta något om dig själv", + "LANGUAGE": "Språk", + "LANGUAGE_DEFAULT": "-- använda standardspråk --", + "THEME": "Tema", + "THEME_DEFAULT": "-- använd standardtema --" + } + }, + "WIZARD": { + "SECTION_TITLE_CHOOSE_TEMPLATE": "Välj en mall", + "CHOOSE_TEMPLATE_TEXT": "Vilken mall skulle passa bättre i det här projektet? ", + "SECTION_TITLE_CREATE_PROJECT": "Skapa projekt", + "CREATE_PROJECT_TEXT": "Fräscht och rent. Så trevligt!", + "PROGRESS_TEMPLATE_SELECTION": "Urval mallar", + "PROGRESS_NAME_DESCRIPTION": "Namn och beskrivning" + }, + "WIKI": { + "PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}", + "PAGE_DESCRIPTION": "Senaste ändringar utförd {{lastModifiedDate}} ({{totalEditions}} editions in total) Innehållt: {{ wikiPageContent }}", + "DATETIME": "YYYY-MM-DD HH:mm", + "PLACEHOLDER_PAGE": "Skriv din wiki-sida", + "REMOVE": "Ta bort den här wiki-sidan", + "DELETE_LIGHTBOX_TITLE": "Ta bort Wiki-sida", + "NAVIGATION": { + "SECTION_NAME": "Länkar", + "ACTION_ADD_LINK": "Lägg till länk" + }, + "SUMMARY": { + "TIMES_EDITED": "gånger
ändrad", + "LAST_EDIT": "senaste
ändring", + "LAST_MODIFICATION": "senast modifierad" + } + }, + "HINTS": { + "SECTION_NAME": "Tips", + "LINK": "Om du önskar att veta hur du ska använda det - besök vår hjälpsida ", + "LINK_TITLE": "Besök vår hjälpsida", + "HINT1_TITLE": "Visste du att du kan importera och exportera projekten?", + "HINT1_TEXT": "Det gör det möjligt att extrahera alla dina data från en Taiga och flytta det till en annan. ", + "HINT2_TITLE": "Visste du att du kan skapa anpassade fält?", + "HINT2_TEXT": "Arbetsgrupper kan skapa anpassade flexibla fält för att använda specifika data som passar arbetsflödet. ", + "HINT3_TITLE": "Omorganizera dina projekt så de blir mera relevanta för dig", + "HINT3_TEXT": "De 10 projekten är listad och du har åtkomst till dem på verktyglinjen på toppen av sidan. ", + "HINT4_TITLE": "Did you forget what were you working on?", + "HINT4_TEXT": "Du behöver inte bekymra dig. På vårt instrumentpanel vill du hitta öppna uppgifter, ärenden och användarhistorier sorterad efter hur du arbetar med dem. " + }, + "TIMELINE": { + "UPLOAD_ATTACHMENT": "{{username}} har laddat upp en ny bilaga i {{obj_name}}", + "US_CREATED": "{{username}} har skapat en ny US {{obj_name}} i {{project_name}}", + "ISSUE_CREATED": "{{username}} har skapat en ny uppgift {{obj_name}} i {{project_name}}", + "TASK_CREATED": "{{username}} har skapat en ny uppgift {{obj_name}} i {{project_name}}", + "TASK_CREATED_WITH_US": "{{username}} har skapat en ny uppgift {{obj_name}} i {{project_name}} som hör till US {{us_name}}", + "WIKI_CREATED": "{{username}} skapade en ny wiki-sida {{obj_name}} i {{project_name}}", + "MILESTONE_CREATED": "{{username}} har skapad en ny sprint {{obj_name}} i {{project_name}}", + "NEW_PROJECT": "{{username}} skapade projektet {{project_name}}", + "MILESTONE_UPDATED": "{{username}} har uppdaterad sprinten {{obj_name}}", + "US_UPDATED": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" i US {{obj_name}}", + "US_UPDATED_WITH_NEW_VALUE": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för US {{obj_name}} till {{new_value}}", + "US_UPDATED_POINTS": "{{username}} har uppdaterad '{{role_name}}' poäng av US {{obj_name}} till {{new_value}}", + "ISSUE_UPDATED": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" i ärendet {{obj_name}}", + "ISSUE_UPDATED_WITH_NEW_VALUE": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för ärendet {{obj_name}} till {{new_value}}", + "TASK_UPDATED": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för uppgiften {{obj_name}} till {{new_value}}", + "TASK_UPDATED_WITH_NEW_VALUE": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för uppgiften {{obj_name}} till {{new_value}}", + "TASK_UPDATED_WITH_US": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för uppgiften {{obj_name}} som tillhör US {{us_name}}", + "TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}} har uppdaterad egenskapen \"{{field_name}}\" för uppgiften {{obj_name}} som tillhör US {{us_name}} till {{new_value}}", + "WIKI_UPDATED": "{{username}} har uppdaterad wiki-sidan {{obj_name}}", + "NEW_COMMENT_US": "{{username}} har kommenterad i {{obj_name}}", + "NEW_COMMENT_ISSUE": "{{username}} har kommenterad i ärendet {{obj_name}}", + "NEW_COMMENT_TASK": "{{username}} har kommenterad uppgiften {{obj_name}}", + "NEW_MEMBER": "{{project_name}} har en ny medlem", + "US_ADDED_MILESTONE": "{{username}} har lagt till US {{obj_name}} till {{sprint_name}}", + "US_MOVED": "{{username}} har flyttat US {{obj_name}}", + "US_REMOVED_FROM_MILESTONE": "{{username}} har lagt till US {{obj_name}} till inkorgen", + "BLOCKED": "{{username}} har blockerad {{obj_name}}", + "UNBLOCKED": "{{username}} har tagit bort blockeringen för {{obj_name}}", + "NEW_USER": "{{username}} har blitt medlem i Taiga" + }, + "LEGAL": { + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "When creating a new account, you agree to our
terms of service and privacy policy." + }, + "EXTERNAL_APP": { + "PAGE_TITLE": "En extern app kräver verifiering", + "PAGE_DESCRIPTION": "En extern app kräver verifiering", + "AUTHORIZATION_REQUEST": "Auktorisera {{application}} för att använda det med ditt Taiga-konto?", + "LOGIN_WITH_ANOTHER_USER": "Logga in med ett annat användarnamn", + "AUTHORIZE_APP": "Auktorisera appen", + "CANCEL": "Avbryt" + }, + "JOYRIDE": { + "NAV": { + "NEXT": "Nästa", + "BACK": "Baksida", + "SKIP": "Skip", + "DONE": "Färdig" + }, + "DASHBOARD": { + "STEP1": { + "TITLE": "Ditt projekt", + "TEXT": "välkommen! Her hittar du projekt du är involverad i." + }, + "STEP2": { + "TITLE": "Arbetar med", + "TEXT": "Här kan du hitta användarhistorier, uppgifter och ärenden som du arbetar på." + }, + "STEP3": { + "TITLE": "Bevakar", + "TEXT1": "And right here you will find the ones in your projects that you want to know about.", + "TEXT2": "Du arbetar redan med Taiga ;)" + }, + "STEP4": { + "TITLE": "Låt oss börja", + "TEXT1": "Du kan starta att skapa ditt första Taiga-projekt.", + "TEXT2": "Lycka till!" + } + }, + "BACKLOG": { + "STEP1": { + "TITLE": "Uppsummering projekt", + "TEXT1": "Har kan du se ditt projekts status.", + "TEXT2": "Du kanändra varje del av projektinställningarna i Admin." + }, + "STEP2": { + "TITLE": "Produkt-inkorg", + "TEXT": "Inkorgen består av kravlistor (användarhistorier) för projektet. Det är här du planerar dina sprintar." + }, + "STEP3": { + "TITLE": "Sprintar", + "TEXT": "Sprintar är korta tidsperioder (normalt 2 veckor) där specifika arbetsuppgifter ska utföras eller levereras. " + }, + "STEP4": { + "TITLE": "Användarhistorie", + "TEXT": "Det här är kraven på en hög nivå. Du kan lägga till dem i inkorgen och dra dem till sprinten som de ska ligga i." + } + }, + "KANBAN": { + "STEP1": { + "TITLE": "Anpassa ditt arbetsflöde", + "TEXT": "Lägg till de kolumner du behöver för att få en översikt över arbetsflödets statusar genom Admin. " + }, + "STEP2": { + "TITLE": "Användarhistorier & uppgifter", + "TEXT": "Användarhistorier är kraven på en hög nivå. Du kan dra dem till olika kolumner." + }, + "STEP3": { + "TITLE": "Lägg till användarhistorier", + "TEXT1": "Du kanske vill lägga till en enda användarhistorie (lägg till US-ikon) eller en grupp av dem (bulk icon). ", + "TEXT2": "Lycka till!" + } + } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Discover projects", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "Most active", + "MOST_ACTIVE_EMPTY": "There are no ACTIVE projects yet", + "MOST_LIKED": "Most liked", + "MOST_LIKED_EMPTY": "There are no LIKED projects yet", + "VIEW_MORE": "View more", + "RECRUITING": "This project is looking for people", + "FEATURED": "Featured Projects", + "EMPTY": "There are no projects to show with this search criteria.
Try again!", + "FILTERS": { + "ALL": "Alla", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "Looking for people", + "WEEK": "Last week", + "MONTH": "Last month", + "YEAR": "Last year", + "ALL_TIME": "All time", + "CLEAR": "Clear filters" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Skriv något ...", + "ACTION_TITLE": "Sök", + "RESULTS": "Search results" + } + } +} \ No newline at end of file diff --git a/app/locales/taiga/locale-tr.json b/app/locales/taiga/locale-tr.json new file mode 100644 index 00000000..37122c6a --- /dev/null +++ b/app/locales/taiga/locale-tr.json @@ -0,0 +1,1476 @@ +{ + "COMMON": { + "YES": "Evet", + "NO": "Hayır", + "OR": "veya", + "LOADING": "Yükleniyor...", + "LOADING_PROJECT": "Proje yükleniyor...", + "DATE": "DD MM YYYY", + "DATETIME": "DD MM YYYY HH:mm", + "SAVE": "Kaydet", + "CANCEL": "İptal", + "ACCEPT": "Kabul et", + "DELETE": "Sil", + "CREATE": "Oluştur", + "ADD": "Ekle", + "COPY_TO_CLIPBOARD": "Panoya kopyala: Ctrl+C", + "EDIT": "Düzenle", + "DRAG": "Sürükle", + "TAG_LINE": "Çevik, ücretsiz ve açık kaynak proje yönetim aracınız", + "TAG_LINE_2": "PROJENİ SEV", + "BLOCK": "Engelle", + "BLOCK_TITLE": "Mesela, bu maddenin çözülemeyen bir bağlantısı varsa engelleyin", + "BLOCKED": "Engelli", + "UNBLOCK": "Engellemeyi kaldır", + "UNBLOCK_TITLE": "Bu maddeden engellemeyi kaldır", + "BLOCKED_NOTE": "Bu niye engelli?", + "BLOCKED_REASON": "Lütfen sebebini açıklayın", + "CREATED_BY": "{{fullDisplayName}} tarafından oluşturuldu", + "FROM": "den", + "TO": "kime", + "CLOSE": "kapat", + "GO_HOME": "Ana sayfaya götür beni", + "PLUGINS": "Eklentiler", + "BETA": "Betadayız!", + "ONE_ITEM_LINE": "Her satıra bir kalem...", + "NEW_BULK": "Yeni toplu ekleme", + "RELATED_TASKS": "İlişkili görevler", + "LOGOUT": "Çıkış", + "EXTERNAL_USER": "bir dış kullanıcı", + "GENERIC_ERROR": "Honki ponkilerimizden biri derki; {{error}}.", + "IOCAINE_TEXT": "Bir görev size ağır geldi ve bunaldınız mı? Diğerlerinin bu durumdan haberi olması için bir görevi düzenlerken baldıran zehrinin üzerine tıklayın. Nasıl ki zaman zaman ekstra meydan okumalarla bir işte gittikce iyi olmanız mümkünse, zaman içerisinde küçük dozlar alarak bu ölümcül zehre de bağışıklık kazanabilmek mümkün!", + "CLIENT_REQUIREMENT": "Müşteri gereksinimi, daha önceden belli olmayan ama projeye dahil olması gereken bir gereksinimdir", + "TEAM_REQUIREMENT": "Takım gereksinimi, müsteriyi ilgilendirmeyen, ama projede yer alması zorunlu olan bir gereksinimdir.", + "CAPSLOCK_WARNING": "Dikkat! Büyük harflerle yazıyorsunuz ve bu girdi büyüklük küçüklük duyarlı.", + "FORM_ERRORS": { + "DEFAULT_MESSAGE": "Bu değer geçersiz gözüküyor", + "TYPE_EMAIL": "Bu değer geçerli bir e-posta adresi olmalı.", + "TYPE_URL": "Bu değer geçerli bir URL şeklinde olmalı", + "TYPE_URLSTRICT": "Bu değer geçerli bir URL şeklinde olmalı", + "TYPE_NUMBER": "Bu değer geçerli bir sayı şeklinde olmalı.", + "TYPE_DIGITS": "Bu değer rakam olmalı", + "TYPE_DATEISO": "Bu değer geçerli bir tarih olmalı (YYYY-MM-DD).", + "TYPE_ALPHANUM": "Bu değer alfanümerik olmalıdır.", + "TYPE_PHONE": "Bu değer geçerli bir telefon numarası şeklinde olmalı", + "NOTNULL": "Bu değer boş olmamalı", + "NOT_BLANK": "Bu değer boş olmamalı", + "REQUIRED": "Bu değer gerekli.", + "REGEXP": "Bu değer geçersiz gözüküyor", + "MIN": "Bu değer %s e eşit yada daha büyük olmalı.", + "MAX": "Bu değer %s den az ya da eşit olmalı.", + "RANGE": "Bu değer %s ve %s aralığında olmalıdır.", + "MIN_LENGTH": "Bu değer çok kısa. Karakter sayısı en az %s olmalıdır.", + "MAX_LENGTH": "Bu değer çok uzun. Karakter sayısı %s kadar ya da daha az olmalıdır.", + "RANGE_LENGTH": "Bu değer uzunluğu geçersiz. Karakter sayısı %s ve %s aralığında olmalıdır.", + "MIN_CHECK": "En az %s seçenek belirtmek zorundasınız.", + "MAX_CHECK": "%s seçenek ya da daha azını seçmek zorunlu.", + "RANGE_CHECK": "%s ve %s seçenekleri arasında bir seçim yapmak zorunludu.", + "EQUAL_TO": "Bu değer aynı olmalı." + }, + "PICKERDATE": { + "FORMAT": "DD MM YYYY", + "IS_RTL": "yanlış", + "FIRST_DAY_OF_WEEK": "1", + "PREV_MONTH": "Önceki Ay", + "NEXT_MONTH": "Gelecek Ay", + "MONTHS": { + "JAN": "Ocak", + "FEB": "Şubat", + "MAR": "Mart", + "APR": "Nisan", + "MAY": "Mayıs", + "JUN": "Haziran", + "JUL": "Temmuz", + "AUG": "Ağustos", + "SEP": "Eylül", + "OCT": "Ekim", + "NOV": "Kasım", + "DEC": "Aralık" + }, + "WEEK_DAYS": { + "SUN": "Pazar", + "MON": "Pazartesi", + "TUE": "Salı", + "WED": "Çarşamba", + "THU": "Perşembe", + "FRI": "Cuma", + "SAT": "Cumartesi" + }, + "WEEK_DAYS_SHORT": { + "SUN": "Pzr", + "MON": "Pzt", + "TUE": "Salı", + "WED": "Çar", + "THU": "Per", + "FRI": "Cum", + "SAT": "Cmt" + } + }, + "SEE_USER_PROFILE": " {{username }} profilini gör", + "USER_STORY": "Kullanıcı hikayesi", + "TASK": "Görev", + "ISSUE": "Sorun", + "TAGS": { + "PLACEHOLDER": "Ben O'yum! Etiketle beni...", + "DELETE": "Etiket sil", + "ADD": "Etiket ekle" + }, + "DESCRIPTION": { + "EMPTY": "Boş alan oldukça sıkıcı oluyor... daha betimleyici olun...", + "NO_DESCRIPTION": "Bir tanım henüz yok" + }, + "FIELDS": { + "SUBJECT": "Konu", + "NAME": "İsim", + "URL": "URL", + "DESCRIPTION": "TanımTanım", + "VALUE": "Değer", + "SLUG": "Satır", + "COLOR": "Renk", + "IS_CLOSED": "Kapatılsın?", + "STATUS": "Durum ", + "TYPE": "Tip ", + "SEVERITY": "Önem Derecesi", + "PRIORITY": "Öncelik", + "ASSIGNED_TO": "Atanmış", + "POINTS": "Puanlar", + "BLOCKED_NOTE": "engel notu", + "IS_BLOCKED": "engellendi ", + "REF": "Ref", + "VOTES": "Oylar" + }, + "ROLES": { + "ALL": "Hepsi" + }, + "ASSIGNED_TO": { + "NOT_ASSIGNED": "Atanmamış", + "ASSIGN": "Ata", + "DELETE_ASSIGNMENT": "Atama sil", + "REMOVE_ASSIGNED": "Atanmışı sil", + "TOO_MANY": "...çok sayıda kullanıcı, filtrelemeye devam", + "CONFIRM_UNASSIGNED": "Atanmamış şekilde bırakmak istediğinizden emin misiniz?", + "TITLE_ACTION_EDIT_ASSIGNMENT": "Atama düzenle", + "SELF": "Bana ata" + }, + "STATUS": { + "CLOSED": "Kapalı", + "OPEN": "Aç" + }, + "WATCHERS": { + "WATCHERS": "İzleyiciler", + "ADD": "İzleyicileri ekle", + "TITLE_ADD": "İzleyiciler listesine bir proje üyesi ekle", + "DELETE": "izleyici sil", + "TITLE_LIGHTBOX_DELETE_WARTCHER": "İzleyici sil" + }, + "WATCH_BUTTON": { + "WATCH": "İzle", + "WATCHING": "İzliyor", + "UNWATCH": "İzleme", + "WATCHERS": "İzleyiciler", + "BUTTON_TITLE": "Bu madeyi izle/izleme ", + "COUNTER_TITLE": "{total, plural, one{bir takipçi} other{# takipçi}}" + }, + "VOTE_BUTTON": { + "UPVOTE": "Oy ver", + "UPVOTED": "Oy verildi", + "DOWNVOTE": "Karşı oy ver", + "VOTERS": "Oylayıcılar", + "BUTTON_TITLE": "Bu konuya oy/karşı oy ver", + "COUNTER_TITLE": "{total, plural, one{bir oy} other{# oy}}" + }, + "CUSTOM_ATTRIBUTES": { + "CUSTOM_FIELDS": "Özel Alanlar", + "SAVE": "Özel Alan Kaydet", + "EDIT": "Özel Alan Düzenle", + "DELETE": "özel öznitelik sil", + "CONFIRM_DELETE": "Bu özel alandaki tüm bilgiler silinecek.\nDevam etmek istediğinize emin misiniz?" + }, + "FILTERS": { + "TITLE": "filtreler", + "INPUT_PLACEHOLDER": "Konu yada referans", + "TITLE_ACTION_FILTER_BUTTON": "ara", + "BREADCRUMB_TITLE": "kategorilere dön", + "BREADCRUMB_FILTERS": "Filtreler", + "BREADCRUMB_STATUS": "durum" + }, + "WYSIWYG": { + "H1_BUTTON": "İlk Düzey Başlık", + "H1_SAMPLE_TEXT": "Başlığınız...", + "H2_BUTTON": "İkinci Düzey Başlık", + "H2_SAMPLE_TEXT": "Başlığınız...", + "H3_BUTTON": "Üçüncü Düzey Başlık", + "H3_SAMPLE_TEXT": "Başlığınız...", + "BOLD_BUTTON": "Kalın", + "BOLD_BUTTON_SAMPLE_TEXT": "Yazınız buraya...", + "ITALIC_BUTTON": "Eğik", + "ITALIC_SAMPLE_TEXT": "Yazınız buraya...", + "STRIKE_BUTTON": "Üstünü çiz", + "STRIKE_SAMPLE_TEXT": "Yazınız buraya..", + "BULLETED_LIST_BUTTON": "Maddeler", + "BULLETED_LIST_SAMPLE_TEXT": "Yazınız buraya...", + "NUMERIC_LIST_BUTTON": "Sayısal Liste", + "NUMERIC_LIST_SAMPLE_TEXT": "Yazınız buraya..", + "PICTURE_BUTTON": "Resim", + "PICTURE_SAMPLE_TEXT": "Resminize alternatif yazıyı buraya...", + "LINK_BUTTON": "Bağlantı", + "LINK_SAMPLE_TEXT": "Bağlantınız...", + "QUOTE_BLOCK_BUTTON": "Alıntı Bloku", + "QUOTE_BLOCK_SAMPLE_TEXT": "Metni buraya yazın...", + "CODE_BLOCK_BUTTON": "Kod Bloğu", + "CODE_BLOCK_SAMPLE_TEXT": "Yazınız buraya..", + "PREVIEW_BUTTON": "Ön izleme", + "EDIT_BUTTON": "Düzenle", + "MARKDOWN_HELP": "Markdown yazım kılavuzu" + }, + "PERMISIONS_CATEGORIES": { + "SPRINTS": { + "NAME": "Koşular", + "VIEW_SPRINTS": "Koşuları gör", + "ADD_SPRINTS": "Koşular ekle", + "MODIFY_SPRINTS": "Koşuları değiştir", + "DELETE_SPRINTS": "Koşuları sil" + }, + "USER_STORIES": { + "NAME": "Kullanıcı Hikayeleri", + "VIEW_USER_STORIES": "Kullanıcı hikayelerini gör", + "ADD_USER_STORIES": "Kullanıcı hikayeleri ekle", + "MODIFY_USER_STORIES": "Kullanıcı hikayelerini değiştir", + "DELETE_USER_STORIES": "Kullanıcı hikayelerini sil" + }, + "TASKS": { + "NAME": "Görevler", + "VIEW_TASKS": "Görevler gör", + "ADD_TASKS": "Görevleri ekle", + "MODIFY_TASKS": "Görevleri değiştir", + "DELETE_TASKS": "Görevleri sil" + }, + "ISSUES": { + "NAME": "Sorunlar", + "VIEW_ISSUES": "Sorunları gör", + "ADD_ISSUES": "Sorunları ekle", + "MODIFY_ISSUES": "Sorunları değiştir", + "DELETE_ISSUES": "Sorunları sil" + }, + "WIKI": { + "NAME": "Wiki", + "VIEW_WIKI_PAGES": "Wiki sayfalarını göster", + "ADD_WIKI_PAGES": "Wiki sayfaları ekle", + "MODIFY_WIKI_PAGES": "Wiki sayfalarını değiştir", + "DELETE_WIKI_PAGES": "Wiki sayfalarını sil", + "VIEW_WIKI_LINKS": "Wiki bağlantılarını göster", + "ADD_WIKI_LINKS": "Wiki bağlantıları ekle", + "DELETE_WIKI_LINKS": "Wiki bağlantılarını sil" + } + }, + "META": { + "PAGE_TITLE": "Taiga", + "PAGE_DESCRIPTION": "Taiga; yeni girişimciler, çevik geliştiriciler ve tasarımcılar için çalışmaları gerçekten eğlenceli hale getiren basit, güzel bir proje yönetim platformudur." + } + }, + "LOGIN": { + "PAGE_TITLE": "Giriş - Taiga", + "PAGE_DESCRIPTION": "Yeni girişimciler, çevik geliştiriciler ve tasarımcılar için çalışmaları gerçekten eğlenceli hale getiren basit, güzel bir proje yönetim platformu Taiga ya giriş yapılıyor." + }, + "AUTH": { + "INVITED_YOU": "sizi şu projeye davet etti", + "NOT_REGISTERED_YET": "Henüz kayıt olmadınız mı?", + "REGISTER": "Kayıt ol", + "CREATE_ACCOUNT": "buradan ücretsiz bir hesap oluşturun" + }, + "LOGIN_COMMON": { + "HEADER": "Zaten bir Taiga hesabım var", + "PLACEHOLDER_AUTH_NAME": "Kullanıcı adı yada e-posta (büyük küçük harf duyarlı)", + "LINK_FORGOT_PASSWORD": "Unuttunuz mu?", + "TITLE_LINK_FORGOT_PASSWORD": "Parolanızı mı unuttunuz?", + "ACTION_ENTER": "Giriş", + "ACTION_SIGN_IN": "Giriş", + "PLACEHOLDER_AUTH_PASSWORD": "Parola (büyük küçük harf duyarlı)" + }, + "LOGIN_FORM": { + "ERROR_AUTH_INCORRECT": "Umpa Lumpalarımıza göre, kullanıcı adınızı / e-posta adresinizi ya da parolanızı yanlış girdiniz.", + "SUCCESS": "Umpa Lumpalarımız çok mutlu oldular, Taiga ya hoşgeldiniz." + }, + "REGISTER": { + "PAGE_TITLE": "Kayıt ol - Taiga", + "PAGE_DESCRIPTION": "Yeni girişimciler, çevik geliştiriciler ve tasarımcılar için çalışmaları gerçekten eğlenceli hale getiren basit, güzel bir proje yönetim platformu Taiga da hesabınızı oluşturun." + }, + "REGISTER_FORM": { + "TITLE": "Yeni bir Taiga hesabı oluştur (ücretsiz)", + "PLACEHOLDER_NAME": "Bir kullanıcı adı seçin (büyük küçük harf duyarlı)", + "PLACEHOLDER_FULL_NAME": "Tam adınızı girin", + "PLACEHOLDER_EMAIL": "E-posta adresiniz", + "PLACEHOLDER_PASSWORD": "Parola belirleyin (büyük küçük harf duyarlı)", + "ACTION_SIGN_UP": "Kaydol", + "TITLE_LINK_LOGIN": "Giriş yap", + "LINK_LOGIN": "Zaten üye misiniz? Giriş yapın" + }, + "FORGOT_PASSWORD": { + "PAGE_TITLE": "Parolayı unuttum - Taiga", + "PAGE_DESCRIPTION": "Kullanıcı adınızı ya da e-posta adresinizi girerek yeni bir parola edinin ve Taiga ya yeniden erişin." + }, + "FORGOT_PASSWORD_FORM": { + "TITLE": "Eyvah, parolanızı mı unuttunuz?", + "SUBTITLE": "Yeni bir tane almak için kullanıcı adınızı ya da e-postanızı girin", + "PLACEHOLDER_FIELD": "Kullanıcı adı veya e-posta", + "ACTION_RESET_PASSWORD": "Parolayı Sıfırla", + "LINK_CANCEL": "Boşver, geri gidelim. Sanırım hatırladım.", + "SUCCESS": "Gelen kutunuzu kontrol edin!
Yeni parolanızı oluşturabilmeniz için gerekli yönergeleri içeren bir e-posta gönderdik", + "ERROR": "Honki ponkilerimize göre, siz henüz bir kayıt yaptırmamış durumdasınız." + }, + "CHANGE_PASSWORD": { + "PAGE_TITLE": "Parolayı değiştirin - Taiga", + "PAGE_DESCRIPTION": "Taiga hesabınız için yeni bir parola belirleyin, ha bir de, bol demirli besinlere yönelmek isteyebilirisiniz belki, beyine iyi gelir :P", + "SECTION_NAME": "Parolayı değiştir", + "FIELD_CURRENT_PASSWORD": "Şimdiki parola", + "PLACEHOLDER_CURRENT_PASSWORD": "Şimdiki parolanız (boş da olabilir eger parola belirlemediyseniz)", + "FIELD_NEW_PASSWORD": "Yeni parola", + "PLACEHOLDER_NEW_PASSWORD": "Yeni bir parola yaz", + "FIELD_RETYPE_PASSWORD": "Yeni parolayı yeniden girin", + "PLACEHOLDER_RETYPE_PASSWORD": "Yeni parolayı yeniden giriniz", + "ERROR_PASSWORD_MATCH": "Parolalar uyuşmuyor" + }, + "CHANGE_PASSWORD_RECOVERY_FORM": { + "TITLE": "Yeni bir Taiga parolası oluşturun", + "SUBTITLE": "Bir de, demir yönünden zengin yiyecekler yemek beyninize iyi gelebilir :P", + "PLACEHOLDER_NEW_PASSWORD": "Yeni parola", + "PLACEHOLDER_RE_TYPE_NEW_PASSWORD": "Yeni parolayı yeniden girin", + "ACTION_RESET_PASSWORD": "Parolayı Sıfırla", + "ERROR": "Honki ponkiler parola geri kurtarma isteğinizi bulamıyorlar. Tekrar sormayı deneyin.", + "SUCCESS": "Honki ponkilerimiz yeni parolanızı kaydetti.
Yeni parolanız ile giriş yapın" + }, + "INVITATION": { + "PAGE_TITLE": "Davet kabulü - Taiga", + "PAGE_DESCRIPTION": "Yeni girişimciler, çevik geliştiriciler ve tasarımcılar için çalışmaları gerçekten eğlenceli hale getiren basit, güzel bir proje yönetim platformu Taiga dan yapılan daveti kabul edin." + }, + "INVITATION_LOGIN_FORM": { + "NOT_FOUND": "Umpa Lumpalarımız davetiyenize ulaşamıyor.", + "SUCCESS": "Projeye katılımınız başarıyla gerçekleşti. {{project_name}} 'ye hoş geldiniz.", + "ERROR": "Honki ponkilerimize göre, ya henüz kayıtlı değilsiniz ya da gerçersiz bir parola girdiniz" + }, + "HOME": { + "PAGE_TITLE": "AnaSayfa - Taiga", + "PAGE_DESCRIPTION": "Atanmış ve izlenen kullanıcı hikayeleri, işler ve sorunların yanı sıra ana projeleriniz için Taiga ana sayfanız", + "EMPTY_WORKING_ON": "Bomboş hissettiriyor degil mi? Taiga ile çalışmaya başlayın ve burada üzerinde çalıştığınız hikayeleri, işleri ve sorunları göreceksiniz.", + "EMPTY_WATCHING": "Projelerinizdeki Kullanıcı Hikayelerini, İşleri ve Sorunları takip edin ve değişikliklerden haberdar olun :) ", + "EMPTY_PROJECT_LIST": "Henüz bir projeniz yok", + "WORKING_ON_SECTION": "Üzerinde çalışılıyor", + "WATCHING_SECTION": "İzleniyor", + "DASHBOARD": "Proje Panosu" + }, + "PROJECTS": { + "PAGE_TITLE": "Projelerim - Taiga", + "PAGE_DESCRIPTION": "Tüm projelerinizi içeren bir liste, yenide düzenle ya da yeni bir tane yarat.", + "MY_PROJECTS": "Projelerim" + }, + "ATTACHMENT": { + "SECTION_NAME": "ekler", + "TITLE": " {{ date }} tarihinde {{ fileName }} dosyası yüklendi", + "LIST_VIEW_MODE": "Liste görünümü biçimi", + "GALLERY_VIEW_MODE": "Galeri görünümü biçimi", + "DESCRIPTION": "Kısa bir tanım yaz", + "DEPRECATED": "(kaldırıldı)", + "DEPRECATED_FILE": "Kaldırıldı mı?", + "ADD": "Yeni ek ekleyin {{maxFileSizeMsg}}", + "DROP": "Ekleri buraya bırakın!", + "MAX_FILE_SIZE": "[Max. boyut: {{maxFileSize}}]", + "SHOW_DEPRECATED": "+ kaldırılan ekleri göster", + "HIDE_DEPRECATED": "- kaldırılan ekleri gizle", + "COUNT_DEPRECATED": "({{ counter }} kaldırılmış)", + "MAX_UPLOAD_SIZE": "Karşıya yükleme için en yüksek boyut limiti {{maxFileSize}}", + "DATE": "DD MMM YYYY [at] hh:mm", + "ERROR_UPLOAD_ATTACHMENT": "'{{fileName}}' dosyasını yükleyemedik. {{errorMessage}}", + "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "Ek sil", + "MSG_LIGHTBOX_DELETE_ATTACHMENT": "ek '{{fileName}}'", + "ERROR_DELETE_ATTACHMENT": "Silemiyoruz: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "({{fileSize}}) boyutundaki '{{fileName}}' dosya honki ponkilerimiz için oldukça ağır geldi, ({{maxFileSize}}) boyutundan daha küçük bir şeylerle deneyin", + "FIELDS": { + "IS_DEPRECATED": "kaldırıldı" + } + }, + "PAGINATION": { + "PREVIOUS": "Önceki", + "NEXT": "İleri" + }, + "ADMIN": { + "COMMON": { + "TITLE_ACTION_EDIT_VALUE": "Değeri düzenle", + "TITLE_ACTION_DELETE_VALUE": "Değer sil" + }, + "HELP": "Yardıma mı ihtiyacın var? Destek sayfamızı kontrol edin!", + "PROJECT_DEFAULT_VALUES": { + "TITLE": "Varsayılan Değerler", + "SUBTITLE": "Tüm seçici girdiler için varsayılan değerleri seçin." + }, + "MEMBERSHIPS": { + "TITLE": "Üyeleri yönet", + "PAGE_TITLE": "Üyelikler - {{projectName}}", + "ADD_BUTTON": "+ Yeni üye", + "ADD_BUTTON_TITLE": "Yeni üye ekle" + }, + "PROJECT_EXPORT": { + "TITLE": "Dışarı aktar", + "SUBTITLE": "Yedek olarak saklamak ya da yeni bir proje için temel olarak adına projenizi dışarı aktarın.", + "EXPORT_BUTTON": "Dışarı aktar", + "EXPORT_BUTTON_TITLE": "Projeni dışarı aktar", + "LOADING_TITLE": "Döküm dosyanızı oluşturuyoruz", + "DUMP_READY": "Döküm dosyanız hazır!", + "LOADING_MESSAGE": "Lütfen bu sayfayı kapatmayın", + "ASYNC_MESSAGE": "Hazır olduğunda size bir e-posta göndereceğiz.", + "SYNC_MESSAGE": "Eğer indirme otomatik olarak başlamıyor ise buraya tıklayın.", + "ERROR": "Bizim Honki ponkiler döküm dosyasını oluştururken birkaç hata ile karşılaştı. Lütfen yeniden deneyin.", + "ERROR_BUSY": "Üzgünüz, bizim honki ponkiler şu anda çok meşgul. Lütfen bir kaç dakika içinde yeniden deneyin.", + "ERROR_MESSAGE": "Honki ponkiler döküm dosyanızı oluştururken birkaç problem yaşadı: {{message}}" + }, + "MODULES": { + "TITLE": "Modüller", + "ENABLE": "Etkinleştir", + "DISABLE": "Pasifleştir", + "BACKLOG": "Havuz", + "BACKLOG_DESCRIPTION": "Yeni gelen ve önceliklendirilmiş işler için düzenli bir görünüm elde etmek için kullanıcı hikayelerinizi yönetin.", + "KANBAN": "Kanban", + "KANBAN_DESCRIPTION": "Bu pano ile projenizi güzel bir şekilde düzenleyin.", + "ISSUES": "Sorunlar", + "ISSUES_DESCRIPTION": "Proje ile ilgili hataları, sorular ve geliştirmeleri izle. Hiçbir şeyi kaçırma!!", + "WIKI": "Wiki", + "WIKI_DESCRIPTION": "Diğerleriyle işbirliği yaparak içerik ekleyin, düzenleyin ve silin. Burası projenizin dokümantasyonu için doğru yer.", + "MEETUP": "Toplantı", + "MEETUP_DESCRIPTION": "Video konferans sisteminizi seçin. Yazılımcılar bile yüz yüze görüşmeye ihtiyaç duyarlar.", + "SELECT_VIDEOCONFERENCE": "Bir video konferans sistemi seç", + "SALT_CHAT_ROOM": "Eğer isterseniz sohbet odası adına salt kod ekleyebilirsiniz", + "JITSI_CHAT_ROOM": "Jitsi", + "APPEARIN_CHAT_ROOM": "AppearIn", + "TALKY_CHAT_ROOM": "Talky", + "CUSTOM_CHAT_ROOM": "Özel", + "URL_CHAT_ROOM": "Sohbet odanızın linki" + }, + "PROJECT_PROFILE": { + "PAGE_TITLE": "{{sectionName}} - Proje profili- {{projectName}}", + "PROJECT_DETAILS": "Proje detayları", + "PROJECT_NAME": "Proje adı", + "PROJECT_SLUG": "Proje satırı", + "NUMBER_SPRINTS": "Koşu sayısı (belirsiz bir miktar için 0)", + "NUMBER_US_POINTS": "KH puanları sayısı (belirlenmemiş miktarlar için 0)", + "TAGS": "Etiketler", + "DESCRIPTION": "Tanım", + "RECRUITING": "Bu proje insan arıyor mu?", + "RECRUITING_MESSAGE": "Ne aramıştınız?", + "RECRUITING_PLACEHOLDER": "Aradığınız özellikleri yazın", + "PUBLIC_PROJECT": "Açık Proje", + "PUBLIC_PROJECT_DESC": "Kullanıcılar projenizi bulabilir ve görebilir", + "PRIVATE_PROJECT": "Gizli proje", + "PRIVATE_PROJECT_DESC": "Normalde bu proje gizli olacak", + "PRIVATE_OR_PUBLIC": "Açık ve özel projeler arasındaki fark nedir?", + "DELETE": "Bu projeyi sil", + "LOGO_HELP": "Resim 80×80 piksel boyutuna ölçeklenecek. ", + "CHANGE_LOGO": "Logoyu değiştir", + "ACTION_USE_DEFAULT_LOGO": "Varsayılan resmi kullan" + }, + "REPORTS": { + "TITLE": "Raporlar", + "SUBTITLE": "Kendi raporlarınızı oluşturmak için proje verinizi CSV biçeminde dışarı aktarın.", + "DESCRIPTION": "Kendi raporlarınızı oluşturmak için CSV dosyası olarak indirin veya oluşturulan URL'yi kopyalayıp istediğiniz metin editörü veya hesap tablosu programında açın. Bu sayede tüm verilerinizi kolayca görüp inceleyebileceksiniz.", + "HELP": "Bunu elektronik çizelgemde nasıl kullanayım?", + "REGENERATE_TITLE": "URL değiştir", + "REGENERATE_SUBTITLE": "CSV veri erişim linkini değiştireceksiniz. Önceki link kapatılacak. Emin misiniz?" + }, + "CSV": { + "SECTION_TITLE_US": "kullanıcı hikayeleri raporları", + "SECTION_TITLE_TASK": "görevlere ait raporlar", + "SECTION_TITLE_ISSUE": "sorun raporları", + "DOWNLOAD": "CSV İndir", + "URL_FIELD_PLACEHOLDER": "Lütfen CSV url'ini yeniden oluşturun", + "TITLE_REGENERATE_URL": "CSV url ini yeniden oluştur", + "ACTION_GENERATE_URL": "URL oluştur", + "ACTION_REGENERATE": "Yeniden oluştur" + }, + "CUSTOM_FIELDS": { + "TITLE": "Özel Alanlar", + "SUBTITLE": "Hikayeleriniz, işleriniz ve sorunlarınız için özel alanları tanımlayın", + "US_DESCRIPTION": "Kullanıcı hikayeleri özel alanları", + "US_ADD": "Kullanıcı hikayelerine özel bir alan ekleyin", + "TASK_DESCRIPTION": "Görevlere ait özel alanlar", + "TASK_ADD": "Görevlere özel alan ekle", + "ISSUE_DESCRIPTION": "Sorunlar için özel alanlar", + "ISSUE_ADD": "Sorunlara yeni bir özel alan ekle", + "FIELD_TYPE_TEXT": "Metin", + "FIELD_TYPE_MULTI": "Çoklu-satır", + "FIELD_TYPE_DATE": "Tarih" + }, + "PROJECT_VALUES": { + "PAGE_TITLE": "{{sectionName}} - Proje değerleri- {{projectName}}", + "REPLACEMENT": "Bu değerdeki tüm öğeler yandaki gibi değiştirilecek", + "ERROR_DELETE_ALL": "Bütün değerleri silemezsiniz." + }, + "PROJECT_VALUES_POINTS": { + "TITLE": "Puanlar", + "SUBTITLE": "Kullanıcı hikayelerinin puanları için kestirim değerleri belirtin", + "US_TITLE": "KH puanları", + "ACTION_ADD": "Yeni puan ekle" + }, + "PROJECT_VALUES_PRIORITIES": { + "TITLE": "Öncelikler", + "SUBTITLE": "Sorunların alabileceği öncelik seviyelerini belirtin", + "ISSUE_TITLE": "Sorun öncelikleri", + "ACTION_ADD": "Yeni öncelik ekle" + }, + "PROJECT_VALUES_SEVERITIES": { + "TITLE": "Önem Dereceleri", + "SUBTITLE": "Sorunların alabileceği önem derecelerini tanımlayın", + "ISSUE_TITLE": "Sorun önem dereceleri", + "ACTION_ADD": "Yeni önem derecesi ekle" + }, + "PROJECT_VALUES_STATUS": { + "TITLE": "Durum", + "SUBTITLE": "Hikayeleriniz, işleriniz ve sorunlarınızın alabileceği durumları tanımlayın", + "US_TITLE": "KH Durumları", + "TASK_TITLE": "Görev Durumları", + "ISSUE_TITLE": "Sorun Durumları" + }, + "PROJECT_VALUES_TYPES": { + "TITLE": "Tipler", + "SUBTITLE": "Taleblerinizin olası tiplerini belirtin", + "ISSUE_TITLE": "Sorun türleri", + "ACTION_ADD": "Ekle yeni {{objName}}" + }, + "ROLES": { + "PAGE_TITLE": "Roller - {{projectName}}", + "WARNING_NO_ROLE": "Dikkat edin, projenizdeki rollerden hiçbiri kullanıcı hikayeleri için puan değeri kestirimi yapma yetkisine sahip değil.", + "HELP_ROLE_ENABLED": "Etkileştirildiğinde, bu role atanan üyeler kullanıcı hikayeleri için puan kestirimi yapabilecek", + "DISABLE_COMPUTABLE_ALERT_TITLE": "Bu rol için kestirimi kapat", + "DISABLE_COMPUTABLE_ALERT_SUBTITLE": "{{roleName}} görevi için tahmin izinlerini kaldırırsanız, bu görevdekiler tarafından daha önce yapılmış tüm tahminler silinir", + "DISABLE_COMPUTABLE_ALERT_MESSAGE": "Bu role ait kestirimleri kapatmak istediğinizden emin misiniz?", + "COUNT_MEMBERS": "bu roldeki üyelerin sayısı {{ role.members_count }} ", + "TITLE_DELETE_ROLE": "Rol Sil", + "REPLACEMENT_ROLE": "Bu role sahip kullanıcıların taşınacağı", + "WARNING_DELETE_ROLE": "Dikkat lütfen, bütün rol kestirimleri silinecek", + "ERROR_DELETE_ALL": "Bütün değerleri silemezsiniz", + "EXTERNAL_USER": "Dış kullanıcı" + }, + "THIRD_PARTIES": { + "SECRET_KEY": "Gizli anahtar", + "PAYLOAD_URL": "Paket URL'si", + "VALID_IPS": "Geçerli çıkış IP'leri (virgülle ayrılmış olarak)" + }, + "BITBUCKET": { + "SECTION_NAME": "Bitbucket", + "PAGE_TITLE": "Bitbucket - {{projectName}}", + "INFO_VERIFYING_IP": "Bitbucket talepleri imzalı değil dolayısıyla kaynağı doğrulamanın en iyi yolu IP 'dir. Eğer bu alan boş ise IP doğrulaması yapılamayacaktır. " + }, + "GITLAB": { + "SECTION_NAME": "Gitlab", + "PAGE_TITLE": "Gitlab - {{projectName}}", + "INFO_VERIFYING_IP": "Gitlab istekleri imzalı değiller bu yüzden IP kaynağını doğrulamak en iyi yol. Eğer alan boş bırakılırsa IP doğrulaması yapılmayacak." + }, + "GITHUB": { + "SECTION_NAME": "Github", + "PAGE_TITLE": "Github - {{projectName}}" + }, + "WEBHOOKS": { + "PAGE_TITLE": "Webhooks - {{projectName}}", + "SECTION_NAME": "Webhooks", + "SUBTITLE": "Webhook, Taiga da meydana gelen yorum, kullanıcı hikayeleri gibi olaylar hakkında dış servisleri bilgilendirir...", + "ADD_NEW": "Yeni bir Webhook Ekle", + "TYPE_NAME": "Servis adını girin", + "TYPE_PAYLOAD_URL": "Servis paket URL'sini yazın", + "TYPE_SERVICE_SECRET": "Gizli anahtar servisini girin", + "SAVE": "Webhook Kaydet", + "CANCEL": "Webhook iptal et", + "SHOW_HISTORY": "(Tarihçeyi göster)", + "TEST": "Webhook testi", + "EDIT": "Webhook Düzenle", + "DELETE": "Webhook Sil", + "REQUEST": "Talep", + "RESEND_REQUEST": "İsteği yeniden gönder", + "HEADERS": "Başlıklar", + "PAYLOAD": "Paket", + "RESPONSE": "Cevap", + "DATE": "DD MMM YYYY [at] hh:mm:ss", + "ACTION_HIDE_HISTORY": "(Tarihçe gizle)", + "ACTION_HIDE_HISTORY_TITLE": "Tarihçe detaylarını gizle", + "ACTION_SHOW_HISTORY": "(Tarihçeyi göster)", + "ACTION_SHOW_HISTORY_TITLE": "Tarihçe detaylarını göster", + "WEBHOOK_NAME": " '{{name}}' Webhook'u" + }, + "CUSTOM_ATTRIBUTES": { + "PAGE_TITLE": "{{sectionName}} - Özel Öznitelikler - {{projectName}}", + "ADD": "Özel alan ekle", + "EDIT": "Özel Alan Düzenle", + "DELETE": "Özel Alan Sil", + "SAVE_TITLE": "Özel Alan Kaydet", + "CANCEL_TITLE": "oluşturmayı iptal et", + "SET_FIELD_NAME": "Özel alan adınızı belirtin", + "SET_FIELD_DESCRIPTION": "Özel alanınıza ait tanım girin", + "FIELD_TYPE_DEFAULT": "-- birini seçin --", + "ACTION_UPDATE": "Özel Alanı Güncelle", + "ACTION_CANCEL_EDITION": "Baskıyı iptal et" + }, + "MEMBERSHIP": { + "COLUMN_MEMBER": "Üye", + "COLUMN_ADMIN": "Yönetim", + "COLUMN_ROLE": "Rol", + "COLUMN_STATUS": "Durum", + "STATUS_ACTIVE": "Aktif", + "STATUS_PENDING": "Bekliyor", + "DELETE_MEMBER": "Üye sil", + "RESEND": "Yeniden gönder", + "SUCCESS_SEND_INVITATION": "Davetiyeyi '{{email}}' adresine yeniden gönderdik.", + "ERROR_SEND_INVITATION": "Henüz bir davetiye göndermedik", + "SUCCESS_DELETE": "{{message}} 'i sildik", + "ERROR_DELETE": "Silemiyoruz {{message}}.", + "DEFAULT_DELETE_MESSAGE": "davetiye {{email}} " + }, + "DEFAULT_VALUES": { + "LABEL_POINTS": "Puan seçici için varsayılan değer", + "LABEL_US": "KH durum seçici için varsayılan değer", + "LABEL_TASK_STATUS": "Görev durum seçici için varsayılan değer", + "LABEL_PRIORITY": "Önceli seçicisi için varsayılan değer", + "LABEL_SEVERITY": "Önem derecesi seçicisi için varsayılan değer", + "LABEL_ISSUE_TYPE": "Sorun tipi seçici için varsayılan değer", + "LABEL_ISSUE_STATUS": "Sorun durumu seçici için varsayılan değer" + }, + "STATUS": { + "PLACEHOLDER_WRITE_STATUS_NAME": "Yeni durum için bir isim yaz" + }, + "TYPES": { + "PLACEHOLDER_WRITE_NAME": "Yeni nesne için bir isim yazın" + }, + "US_STATUS": { + "ACTION_ADD_STATUS": "Yeni durum ekle", + "IS_ARCHIVED_COLUMN": "Arşivlensin?", + "WIP_LIMIT_COLUMN": "WIP Sınır", + "PLACEHOLDER_WRITE_NAME": "Yeni durum için bir isim yaz" + }, + "MENU": { + "TITLE": "Yönetim", + "PROJECT": "Proje", + "ATTRIBUTES": "Öznitelikler", + "MEMBERS": "Üyeler", + "PERMISSIONS": "İzinler", + "INTEGRATIONS": "Bütünleştirmeler", + "PLUGINS": "Eklentiler" + }, + "SUBMENU_PROJECT_ATTRIBUTES": { + "TITLE": "Öznitelikler" + }, + "SUBMENU_PROJECT_VALUES": { + "STATUS": "Durum", + "POINTS": "Puanlar", + "PRIORITIES": "Öncelikler", + "SEVERITIES": "Önem Dereceleri", + "TYPES": "Tipler", + "CUSTOM_FIELDS": "Özel alanlar" + }, + "SUBMENU_PROJECT_PROFILE": { + "TITLE": "Proje Profili" + }, + "SUBMENU_ROLES": { + "TITLE": "Roller", + "ACTION_NEW_ROLE": "+ Yeni rol", + "TITLE_ACTION_NEW_ROLE": "Yeni rol ekle" + }, + "SUBMENU_THIDPARTIES": { + "TITLE": "Servisler" + } + }, + "USER": { + "PROFILE": { + "PAGE_TITLE": "{{userFullName}} (@{{userUsername}})", + "EDIT": "Profili Düzenle", + "FOLLOW": "Takip et", + "CLOSED_US": "Kapatılmış KH", + "PROJECTS": "Projeler", + "PROJECTS_EMPTY": "{{username}} kullanıcısının henüz bir projesi yok", + "CONTACTS": "İrtibat Kişileri", + "CONTACTS_EMPTY": "{{username}} kullanıcısının henüz irtibat kişileri yok", + "CURRENT_USER_CONTACTS_EMPTY": "Henüz irtibat kişileriniz yok", + "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "Taiga ile çalışan kişiler otomatik olarak irtibat kişileriniz olacaktır", + "REPORT": "Kötüye Kullanımı Raporla", + "TABS": { + "ACTIVITY_TAB": "Zaman Çizelgesi", + "ACTIVITY_TAB_TITLE": "Bu kullanıcı için tüm eylemleri göster", + "PROJECTS_TAB": "Projeler", + "PROJECTS_TAB_TITLE": "Kullanıcının üyesi olduğu tüm projelerin listesi", + "LIKES_TAB": "Beğeniler", + "LIKES_TAB_TITLE": "Bu kullanıcının tüm beğenilerilerini listele", + "VOTES_TAB": "Oylar", + "VOTES_TAB_TITLE": "Bu kullanıcı tarafından verilmiş tüm oyları görün", + "WATCHED_TAB": "İzlendi", + "WATCHED_TAB_TITLE": "Bu kullanıcı tarafından izlenen herşeyi listele", + "CONTACTS_TAB": "İrtibat Kişileri", + "CONTACTS_TAB_TITLE": "Bu kullanıcının tüm kişilerini listele" + } + }, + "PROFILE_SIDEBAR": { + "TITLE": "Profiliniz", + "DESCRIPTION": "Yaptığınız herşey ve üzerinde çalıştığınız konular buradan görülebilir. Bilgilerinizi zenginleştirmek için güzel bir özgeçmiş ekleyin.", + "ADD_INFO": "Bio yu düzenle" + }, + "PROFILE_FAVS": { + "FILTER_INPUT_PLACEHOLDER": "Birşeyler yazın...", + "FILTER_TYPE_ALL": "Hepsi", + "FILTER_TYPE_ALL_TITLE": "Hepsini göster", + "FILTER_TYPE_PROJECTS": "Projeler", + "FILTER_TYPE_PROJECT_TITLES": "Sadece projeleri görüntüle", + "FILTER_TYPE_USER_STORIES": "Hikayeler", + "FILTER_TYPE_USER_STORIES_TITLES": "Sadece kullanıcı hikayelerini göster", + "FILTER_TYPE_TASKS": "Görevler", + "FILTER_TYPE_TASK_TITLES": "Sadece görevleri göster", + "FILTER_TYPE_ISSUES": "Sorunlar ", + "FILTER_TYPE_ISSUES_TITLE": "Sadece sorunları göster", + "EMPTY_TITLE": "Görünüşe göre burada gösterilecek bir şey yok." + } + }, + "PROJECT": { + "PAGE_TITLE": "{{projectName}}", + "WELCOME": "Hoşgeldiniz", + "SECTION_PROJECTS": "Projeler", + "HELP": "En çok kullanılan projelerinizi en üste koymak için yeniden sıralayın.
Top 10 projeler gezinti çubuğunun proje listesinin başında gösterilecektir.", + "PRIVATE": "Gizli proje", + "LOOKING_FOR_PEOPLE": "Bu proje ahalisini arıyor", + "FANS_COUNTER_TITLE": "{total, plural, one{bir meraklı} other{# meraklı}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{bir takipçi} other{# takipçi}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{bir üye} other{# üye}}", + "STATS": { + "PROJECT": "proje
puanlar", + "DEFINED": "tanımlı
puanlar", + "ASSIGNED": "atanmış
puanlar", + "CLOSED": "kapatılmış
puanlar" + }, + "SECTION": { + "SEARCH": "Ara", + "TIMELINE": "Zaman Çizelgesi", + "BACKLOG": "Havuz", + "KANBAN": "Kanban", + "ISSUES": "Sorunlar", + "WIKI": "Wiki", + "TEAM": "Takım", + "MEETUP": "Toplantı", + "ADMIN": "Yönetim" + }, + "NAVIGATION": { + "SECTION_TITLE": "Projeleriniz", + "PLACEHOLDER_SEARCH": "Aranan konum...", + "ACTION_CREATE_PROJECT": "Proje Oluştur", + "ACTION_IMPORT_PROJECT": "Projeyi İçe Aktar", + "MANAGE_PROJECTS": "Projeleri yönet", + "TITLE_CREATE_PROJECT": "Proje oluştur", + "TITLE_IMPORT_PROJECT": "Projeyi İçe Aktar", + "TITLE_PRVIOUS_PROJECT": "Önceki projeleri göster", + "TITLE_NEXT_PROJECT": "Sonraki projeleri göster", + "HELP_TITLE": "Taiga Destek Sayfası", + "HELP": "Yardım", + "HOMEPAGE": "Ana sayfa", + "FEEDBACK_TITLE": "Geribildirim Gönder", + "FEEDBACK": "Geribildirim", + "NOTIFICATIONS_TITLE": "Bildirim ayarlarını düzenle", + "NOTIFICATIONS": "Bildirimler", + "ORGANIZATIONS_TITLE": "Organizasyonunuzu düzenleyin", + "ORGANIZATIONS": "Organizasyonları düzenle", + "SETTINGS_TITLE": "Ayarlarınızı düzenleyin", + "SETTINGS": "Ayarlar", + "VIEW_PROFILE_TITLE": "Profili Görüntüle", + "VIEW_PROFILE": "Profili Görüntüle", + "EDIT_PROFILE_TITLE": "Profilini düzenle", + "EDIT_PROFILE": "Profil Düzenle", + "CHANGE_PASSWORD_TITLE": "Parolayı değiştir", + "CHANGE_PASSWORD": "Parolayı değiştir", + "DASHBOARD_TITLE": "Pano", + "DISCOVER_TITLE": "Trend projeleri keşfet", + "DISCOVER": "Keşfet", + "ACTION_REORDER": "Yeniden sıralamak için sürükleyin ve bırakın" + }, + "IMPORT": { + "TITLE": "Proje İçe Aktarılıyor", + "UPLOADING_FILE": "Döküm dosyanız karşıya yükleniyor", + "DESCRIPTION": "Bu işlem biraz sürecek, lütfen pencereyi kapatmayın.", + "ASYNC_IN_PROGRESS_TITLE": "Bizim honki ponkiler projenizi içeriye aktarıyor", + "ASYNC_IN_PROGRESS_MESSAGE": "Bu işlem bir kaç dakika sürecek
Hazır olduğunda bir e-posta göndereceğiz", + "UPLOAD_IN_PROGRESS_MESSAGE": "{{totalSize}} in {{uploadedSize}} kadarı karşıya yüklendi", + "ERROR": "Sarı çizmeli memmet ağamız döküm dosyanızı içeri aktarırken birkaç problem yaşadı. Lütfen yeniden deneyin.", + "ERROR_TOO_MANY_REQUEST": "Üzgünüm, bizim honki ponkiler şu anda çok meşgul. Lütfen birkaç dakika içinde yeniden deneyin.", + "ERROR_MESSAGE": "Honki ponkilerimiz döküm dosyanızı içeri aktarırken birkaç problem yaşadı: {{error_message}}", + "ERROR_MAX_SIZE_EXCEEDED": "({{fileSize}}) boyutundaki '{{fileName}}' dosya honki ponkilerimiz için oldukça ağır geldi, ({{maxFileSize}}) boyutundan daha küçük bir şeylerle deneyin", + "SYNC_SUCCESS": "Projeniz başarıyla içe aktarıldı" + }, + "LIKE_BUTTON": { + "LIKE": "Beğen", + "LIKED": "Beğendi", + "UNLIKE": "Beğenme", + "BUTTON_TITLE": "Bu projeyi beğen ya da beğenme", + "COUNTER_TITLE": "{total, plural, one{bir meraklı} other{# meraklı}}" + }, + "WATCH_BUTTON": { + "BUTTON_TITLE": "Bu projeyi izle ve bildirim politikasını ayarla", + "WATCH": "İzle", + "WATCHING": "İzliyor", + "COUNTER_TITLE": "{total, plural, one{bir takipçi} other{# takipçi}}", + "OPTIONS": { + "NOTIFY_ALL": "Bütün bildirimleri al", + "NOTIFY_ALL_TITLE": "Bu proje için tüm bildirimleri al", + "NOTIFY_INVOLVED": "Sadece ilgililer", + "NOTIFY_INVOLVED_TITLE": "Sadece ilgilendiğiniz bildirimleri alın", + "UNWATCH": "İzleme", + "UNWATCH_TITLE": "Bu projeyi izleme" + } + } + }, + "LIGHTBOX": { + "DELETE_ACCOUNT": { + "SECTION_NAME": "Taiga Hesabını Sil", + "CONFIRM": "Taiga hesabınızı silmek istediğinizden emin misiniz?", + "SUBTITLE": "Sizi özleyeceğiz! :-(", + "NEWSLETTER_LABEL_TEXT": "Bundan böyle bülteninizi almak istemiyorum." + }, + "DELETE_PROJECT": { + "TITLE": "Proje Sil", + "QUESTION": "Bu projeyi silmek istediğinizden emin misiniz?", + "SUBTITLE": "Bütün proje verileri (kullanıcı hikayeleri, işler, sorunlar, koşular ve viki sayfaları) kaybolacak! :-(", + "CONFIRM": "Evet, kesinlikle eminim" + }, + "ASSIGNED_TO": { + "SELECT": "Atananı seç", + "SEARCH": "Kullanıcıları ara" + }, + "ADD_MEMBER": { + "TITLE": "Yeni üye", + "HELP_TEXT": "Eğer kullanıcılar önceden Taigaya kayıt olmuşlarsa, otomatik olarak ekleneceklerdir. Eğer olmamışlarsa bir davet mektubu alacaklardır." + }, + "CREATE_ISSUE": { + "TITLE": "Sorun Ekle" + }, + "FEEDBACK": { + "TITLE": "Bize birşeyler anlat..", + "COMMENT": "...bir hata, biraz öneri, biraz klas... ya da Taiga ile en kötü kabusunuz", + "ACTION_SEND": "Geribildirim Gönder" + }, + "SEARCH": { + "TITLE": "Ara", + "PLACEHOLDER_SEARCH": "Neye bakıyorsunuz?" + }, + "ADD_EDIT_SPRINT": { + "TITLE": "Yeni koşu", + "PLACEHOLDER_SPRINT_NAME": "koşu adı", + "PLACEHOLDER_SPRINT_START": "Tahmini Başlangıç", + "PLACEHOLDER_SPRINT_END": "Tahmini Bitiş", + "ACTION_DELETE_SPRINT": "Bu koşuyu silmek istiyor musunuz?", + "TITLE_ACTION_DELETE_SPRINT": "koşuyu sil", + "LAST_SPRINT_NAME": "Son koşu {{lastSprint}} ;-)" + }, + "CREATE_EDIT_TASK": { + "TITLE": "Yeni görev", + "PLACEHOLDER_SUBJECT": "Bir görev konusu", + "PLACEHOLDER_STATUS": "Görev durumu", + "OPTION_UNASSIGNED": "Atama Yok", + "PLACEHOLDER_SHORT_DESCRIPTION": "Kısa bir tanım yaz", + "ACTION_EDIT": "Görevi düzenle" + }, + "CREATE_EDIT_US": { + "TITLE": "Yeni KH", + "PLACEHOLDER_DESCRIPTION": "Lütfen bu KH ye betimleyici bilgiler girerek diğerlerinin daha iyi anlamasına yardımcı olunuz.", + "NEW_US": "Yeni kullanıcı hikayesi", + "EDIT_US": "Kullanıcı hikayesini düzenle" + }, + "DELETE_SPRINT": { + "TITLE": "Koşuyu sil" + }, + "CREATE_MEMBER": { + "PLACEHOLDER_INVITATION_TEXT": "(Opsiyonel) Davetinize kişiselleştirilmiş bir metin ekleyin. Yeni üyelerinize tatlı bir şeyler söyleyin ;-)", + "PLACEHOLDER_TYPE_EMAIL": "Bir e-posta girin" + } + }, + "US": { + "PAGE_TITLE": "{{userStorySubject}} - Kullanıcı Hikayesi {{userStoryRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Durum: {{userStoryStatus }}. Tamamlanan {{userStoryProgressPercentage}}% ({{userStoryClosedTasks}} of {{userStoryTotalTasks}} tasks closed). Puanlar: {{userStoryPoints}}. Tanım: {{userStoryDescription}}", + "SECTION_NAME": "Kullanıcı hikayesi detayları", + "LINK_TASKBOARD": "Görev Panosu", + "TITLE_LINK_TASKBOARD": "Görev panosuna git", + "TOTAL_POINTS": "toplam puanlar", + "ADD": "+ Yeni bir Kullanıcı Hikayesi ekle", + "ADD_BULK": "Toplu halde yeni Kullanıcı Hikayeleri ekle", + "PROMOTED": "Bu hikayenin temelindeki sorun:", + "TITLE_LINK_GO_TO_ISSUE": "Talebe git", + "EXTERNAL_REFERENCE": "Bu KH 'ni oluşturulduğu", + "GO_TO_EXTERNAL_REFERENCE": "Kökenine git ", + "BLOCKED": "Bu kullanıcı hikayesi engelli", + "PREVIOUS": "önceki kullanıcı hikayesi", + "NEXT": "sonraki kullanıcı hikayesi", + "TITLE_DELETE_ACTION": "Kullanıcı Hikayesi Sil", + "LIGHTBOX_TITLE_BLOKING_US": "Bizi engelleyen", + "TASK_COMPLETED": "{{totalClosedTasks}}/{{totalTasks}} tamamlanan görevler", + "ASSIGN": "Kullanıcı Hikayesini Ata", + "NOT_ESTIMATED": "Kestirim yapılmamış", + "TOTAL_US_POINTS": "Toplam KH puanları", + "FIELDS": { + "TEAM_REQUIREMENT": "Takım Gereksinimi", + "CLIENT_REQUIREMENT": "İstemci Gereksinimi", + "FINISH_DATE": "Bitiş tarihi" + } + }, + "COMMENTS": { + "DELETED_INFO": "Yorum {{date}} tarihinde {{user}} tarafından silindi", + "TITLE": "Yorumlar", + "COMMENT": "Yorum Yap", + "TYPE_NEW_COMMENT": "Buraya yeni bir yorum yazın", + "SHOW_DELETED": "Silinmiş yorumları göster", + "HIDE_DELETED": "Silinmiş yorumu gizle", + "DELETE": "Yorumu sil", + "RESTORE": "Yorumu geri yükle" + }, + "ACTIVITY": { + "SHOW_ACTIVITY": "Eylemleri göster", + "DATETIME": "DD MMM YYYY HH:mm", + "SHOW_MORE": "+ Önceki girdileri göster ({{showMore}} daha fazla)", + "TITLE": "Aktivite", + "REMOVED": "silindi", + "ADDED": "eklendi", + "US_POINTS": "KH puanları ({{name}})", + "NEW_ATTACHMENT": "yeni ek", + "DELETED_ATTACHMENT": "ek sil", + "UPDATED_ATTACHMENT": "güncellenmiş ek {{filename}}", + "DELETED_CUSTOM_ATTRIBUTE": "silinmiş özel öznitelik", + "SIZE_CHANGE": "Yapılan {size, plural, one{tek değişiklik} other{# değişiklikler}}", + "VALUES": { + "YES": "evet", + "NO": "hayır", + "EMPTY": "boş", + "UNASSIGNED": "atama yok" + }, + "FIELDS": { + "SUBJECT": "konu", + "NAME": "isim", + "DESCRIPTION": "tanım", + "CONTENT": "içerik", + "STATUS": "durum", + "IS_CLOSED": "kapatıldı", + "FINISH_DATE": "bitiş tarihi", + "TYPE": "tip", + "PRIORITY": "öncelik", + "SEVERITY": "önem derecesi", + "ASSIGNED_TO": "atanmış", + "WATCHERS": "izleyiciler", + "MILESTONE": "koşu", + "USER_STORY": "kullanıcı hikayesi", + "PROJECT": "proje", + "IS_BLOCKED": "engellendi", + "BLOCKED_NOTE": "engel notu", + "POINTS": "puanlar", + "CLIENT_REQUIREMENT": "istemci gereksinimi", + "TEAM_REQUIREMENT": "takım gereksinimi", + "IS_IOCAINE": "baldıran zehri", + "TAGS": "etiketler", + "ATTACHMENTS": "ekler", + "IS_DEPRECATED": "kaldırıldı", + "ORDER": "sıra", + "BACKLOG_ORDER": "havuz sıralaması", + "SPRINT_ORDER": "koşu sırası", + "KANBAN_ORDER": "kanban sırası", + "TASKBOARD_ORDER": "Görev panosu sırası", + "US_ORDER": "kh sırası" + } + }, + "BACKLOG": { + "PAGE_TITLE": "Havuz - {{projectName}}", + "PAGE_DESCRIPTION": "{{projectName}} projesinin koşuları ve kullanıcı hikayeleriyle birlikte havuz paneli: {{projectDescription}}", + "SECTION_NAME": "Havuz", + "CUSTOMIZE_GRAPH": "Havuz grafiğinizi özelleştirin", + "CUSTOMIZE_GRAPH_TEXT": "Projenizin gelişimini görebileceğiniz güzel bir grafik için koşu ve puanları ayarlayın", + "CUSTOMIZE_GRAPH_ADMIN": "Yönetim", + "CUSTOMIZE_GRAPH_TITLE": "Yönetimden puan ve koşuları ayarlayın", + "MOVE_US_TO_CURRENT_SPRINT": "Şimdiki Koşuya Taşı", + "MOVE_US_TO_LATEST_SPRINT": "Son koşuya taşı", + "SHOW_FILTERS": "Filtreleri göster", + "SHOW_TAGS": "Etiketleri göster", + "EMPTY": "Havuz bomboş!", + "CREATE_NEW_US": "Yeni bir KH oluştur", + "CREATE_NEW_US_EMPTY_HELP": "Yeni bir kullanıcı hikayesi oluşturmak isteyebilirsiniz", + "EXCESS_OF_POINTS": "Puan bolluğu", + "PENDING_POINTS": "Bekleyen Puanlar", + "CLOSED_POINTS": "kapatıldı", + "COMPACT_SPRINT": "Kısa koşu", + "GO_TO_TASKBOARD": " {{::name}} görev panosuna git ", + "EDIT_SPRINT": "Koşuyu düzenle", + "TOTAL_POINTS": "toplam", + "STATUS_NAME": "Durum Adı", + "SORTABLE_FILTER_ERROR": "Filtreler açıkken sürükle bırak yapamazsınız", + "DOOMLINE": "Proje Kapsamı", + "CHART": { + "XAXIS_LABEL": "Koşular", + "YAXIS_LABEL": "Puanlar", + "OPTIMAL": "\"{{sprintName}}\" koşusu için ideal puan {{value}} olmalı", + "REAL": "\"{{sprintName}}\" koşusunda gerçekte kalan puan {{value}}", + "INCREMENT_TEAM": " {{sprintName}} koşusu için müşteri gereksinimleri nedeniyle artırılan puan {{value}}", + "INCREMENT_CLIENT": " {{sprintName}} koşusu için takım gereksinimleri nedeniyle artırılan puan {{value}}" + }, + "TAGS": { + "TOGGLE": "Etiket görünürlüğünü değiştir", + "SHOW": "Etiketleri göster", + "HIDE": "Etiketleri gizler" + }, + "TABLE": { + "COLUMN_US": "Kullanıcı Hikayeleri ", + "TITLE_COLUMN_POINTS": "Her rol için görünüm seç" + }, + "SPRINT_SUMMARY": { + "TOTAL_POINTS": "toplam
puan", + "COMPLETED_POINTS": "tamamlanmış
puanlar", + "OPEN_TASKS": "açık
görevler", + "CLOSED_TASKS": "kapatılmış
görevler", + "IOCAINE_DOSES": "baldıran zehri
dozu", + "SHOW_STATISTICS_TITLE": "İstatistikleri göster", + "TOGGLE_BAKLOG_GRAPH": "Eritme grafiğini göster/gizle" + }, + "SUMMARY": { + "PROJECT_POINTS": "proje
puanları", + "DEFINED_POINTS": "tanımlı
puanlar", + "CLOSED_POINTS": "kapatılmış
puanlar", + "POINTS_PER_SPRINT": "puanlar /
koşu" + }, + "FILTERS": { + "TOGGLE": "Filtrelerin görünürlüğünü değiştir", + "TITLE": "Filtreler", + "REMOVE": "Filtreleri Sil", + "HIDE": "Filtreleri Gizle", + "SHOW": "Filtreleri Göster", + "FILTER_CATEGORY_STATUS": "Durum", + "FILTER_CATEGORY_TAGS": "Etiketler " + }, + "SPRINTS": { + "TITLE": "KOŞULAR", + "DATE": "DD MM YYYY", + "LINK_TASKBOARD": "Koşu İş Panosu", + "TITLE_LINK_TASKBOARD": "\"{{name}}\" görev panosuna git", + "NUMBER_SPRINTS": "
koşular", + "EMPTY": "Henüz hiç koşu yok", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "Bu koşuda hiç kullanıcı hikayesi yok.", + "WARNING_EMPTY_SPRINT": "Yeni bir koşu başlatmak icin havuzdan buraya hikaye taşıyın", + "TITLE_ACTION_NEW_SPRINT": "Yeni koşu ekle", + "TEXT_ACTION_NEW_SPRINT": "Projenizde yeni bri koşu oluşturmak isteyebilirsiniz", + "ACTION_SHOW_CLOSED_SPRINTS": "Kapatılmış koşuları göster", + "ACTION_HIDE_CLOSED_SPRINTS": "Kapatılmış koşuları gizle" + } + }, + "ERROR": { + "TEXT1": "Birşeyler oldu ve bizim Honki ponkilerimiz üzerinde çalışıyor.", + "NOT_FOUND": "Bulunamadı", + "NOT_FOUND_TEXT": "Hata 404. Baktığınız sayfa mevcut değil. Belki TAIGA ana sayfasına geri dönerek aradığınızı bulabilirsiniz.", + "PERMISSION_DENIED": "İzin verilmedi", + "PERMISSION_DENIED_TEXT": "Bu sayfaya erişim izniniz yok.", + "VERSION_ERROR": "Honki ponkilerimiz yaptığınız değişiklikleri uygulamadan önce Taiga da çalışan birileri farklı bir değişiklik yapmış. Lütfen sayfayı yeniden yükleyin ve değişikliklerinizi yeniden uygulayın(yenileme ile değişiklikler kaybolabilir)." + }, + "TASKBOARD": { + "PAGE_TITLE": "{{sprintName}} - Koşu İş Panosu - {{projectName}}", + "PAGE_DESCRIPTION": "{{projectName}} projesinin koşusu {{sprintName}} ( {{startDate}} - {{endDate}} tarihleri arası) . %{{completedPercentage}} tamam. ({{totalPoints}} puandan {{completedPoints}} bitmiş). {{totalTasks}} işten {{openTasks}} tanesi açık.", + "SECTION_NAME": "Görev Panosu", + "TITLE_ACTION_ADD": "Yeni bir Görev ekle", + "TITLE_ACTION_ADD_BULK": "Toplu halde yeni bir kaç görev ekle", + "TITLE_ACTION_ASSIGN": "Görev ata", + "TITLE_ACTION_EDIT": "Görevi düzenle", + "PLACEHOLDER_CARD_TITLE": "Bu bir görev olabilir", + "PLACEHOLDER_CARD_TEXT": "Hikayeleri ve görevleri birbirinden ayırın ve öyle izleyin", + "TABLE": { + "COLUMN": "Kullanıcı hikayesi", + "TITLE_ACTION_FOLD": "Sütun Katla", + "TITLE_ACTION_UNFOLD": "Sütun katını aç", + "TITLE_ACTION_FOLD_ROW": "Satın Katla", + "TITLE_ACTION_UNFOLD_ROW": "Satır katını aç", + "FIELD_POINTS": "puanlar", + "ROW_UNASSIGED_TASKS_TITLE": "Atanmamış görevler" + }, + "CHARTS": { + "XAXIS_LABEL": "Günler", + "YAXIS_LABEL": "Puanlar", + "OPTIMAL": "{{formattedDate}} günü için ideal kalan puan {{roundedValue}} olmalı", + "REAL": "{{formattedDate}} günü gerçekten kalan puan {{roundedValue}}", + "DATE": "DD MMMM YYYY" + } + }, + "TASK": { + "PAGE_TITLE": "{{taskSubject}} - Görev {{taskRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Durum: {{taskStatus }}. Tanım: {{taskDescription}}", + "SECTION_NAME": "Görev detayları", + "LINK_TASKBOARD": "Görev Panosu", + "TITLE_LINK_TASKBOARD": "Görev panosuna git", + "PLACEHOLDER_SUBJECT": "Yeni görev konusunu gir", + "TITLE_SELECT_STATUS": "Durum Adı", + "OWNER_US": "Bu görevin ait olduğu", + "TITLE_LINK_GO_OWNER": "Kullanıcı hikayesine git", + "ORIGIN_US": "Bu görevin oluşturulduğu", + "TITLE_LINK_GO_ORIGIN": "Kullanıcı hikayesine git", + "BLOCKED": "Bu iş engelli", + "PREVIOUS": "önceki görev", + "NEXT": "sonraki görev", + "TITLE_DELETE_ACTION": "Görev Sil", + "LIGHTBOX_TITLE_BLOKING_TASK": "Engelleyen iş", + "FIELDS": { + "MILESTONE": "Koşu", + "USER_STORY": "Kullanıcı hikayesi", + "IS_IOCAINE": "baldıran zehri" + }, + "ACTION_IOCAINE": "baldıran zehri", + "TITLE_ACTION_IOCAINE": "Bir görev size ağır geldi ve bunaldınız mı? Diğerlerinin bu durumdan haberi olması için bir görevi düzenlerken baldıran zehrinin(temsili) üzerine tıklayın. Nasıl ki zaman zaman ekstra meydan okumalarla bir işte gittikçe iyi olmanız mümkünse, zaman içerisinde küçük dozlar alarak bu ölümcül zehre de bağışıklık kazanabilmek mümkün!" + }, + "NOTIFICATION": { + "OK": "Herşey mükemmel", + "WARNING": "Amanın, birşeyler oldu..", + "WARNING_TEXT": "Honki ponkilerimiz üzgün, yaptığınz değişiklikler kaydedilemedi!", + "SAVED": "Honki ponkilerimiz tüm değişikliklerinizi kaydetti!", + "CLOSE": "Bildirimleri kapat", + "MAIL": "Posta ile Bildirim", + "ASK_DELETE": "Silmek istediğinizden emin misiniz?" + }, + "CANCEL_ACCOUNT": { + "TITLE": "Hesabınızı iptal edin", + "SUBTITLE": "Taigadan ayrıldığınız için üzüldük, umarım kaldığınız sürece sizi memnun kılmışızdır :)", + "PLACEHOLDER_INPUT_TOKEN": "hesap kuponunu iptal et", + "ACTION_LEAVING": "Evet, ayrılıyorum!", + "SUCCESS": "Honki ponkilerimiz hesabınızı sildi" + }, + "CHANGE_EMAIL_FORM": { + "TITLE": "E-postanı değiştir", + "SUBTITLE": "Bir tık daha ve e-postanız güncellenecek!", + "PLACEHOLDER_INPUT_TOKEN": "e-posta kuponunu değiştir", + "ACTION_CHANGE_EMAIL": "E-posta değiştir", + "SUCCESS": "Honki ponkiler e-postanızı güncelledi" + }, + "ISSUES": { + "PAGE_TITLE": "Sorunlar - {{projectName}}", + "PAGE_DESCRIPTION": "{{projectName}} projesinin sorun listesi paneli: {{projectDescription}}", + "LIST_SECTION_NAME": "Sorunlar", + "SECTION_NAME": "Sorun detayları", + "ACTION_NEW_ISSUE": "+ YENİ SORUN", + "ACTION_PROMOTE_TO_US": "Kullanıcı Hikayesine Terfi Ettir", + "PLACEHOLDER_FILTER_NAME": "Filtre adı yazın ve enter a basın", + "PROMOTED": "Bu sorun, kullanıcı hikayesine yükseltildi:", + "EXTERNAL_REFERENCE": "Bu talebin oluşturulduğu ", + "GO_TO_EXTERNAL_REFERENCE": "Kökenine git", + "BLOCKED": "Bu sorun engelli", + "TITLE_PREVIOUS_ISSUE": "önceki sorun", + "TITLE_NEXT_ISSUE": "sonraki sorun", + "ACTION_DELETE": "Sorun sil", + "LIGHTBOX_TITLE_BLOKING_ISSUE": "Engelleyen sorun", + "FIELDS": { + "PRIORITY": "Öncelik", + "SEVERITY": "Önem Durumu", + "TYPE": "Tip" + }, + "CONFIRM_PROMOTE": { + "TITLE": "Bu talebi yeni bir kullanıcı hikayesi olacak şekilde terfi ettirin", + "MESSAGE": "Bu sorundan yeni bir hikaye oluşturmak istediğinize emin misiniz?" + }, + "FILTERS": { + "TITLE": "Filtreler", + "INPUT_SEARCH_PLACEHOLDER": "Konu ya da ref", + "TITLE_ACTION_SEARCH": "Ara", + "ACTION_SAVE_CUSTOM_FILTER": "özel filtre olarak kaydet", + "BREADCRUMB": "Filtreler", + "TITLE_BREADCRUMB": "Filtreler", + "CATEGORIES": { + "TYPE": "Tip", + "STATUS": "Durum", + "SEVERITY": "Önem Derecesi", + "PRIORITIES": "Öncelikler", + "TAGS": "Etiketler", + "ASSIGNED_TO": "Atanmış", + "CREATED_BY": "Oluşturan", + "CUSTOM_FILTERS": "Özel filtreler" + }, + "CONFIRM_DELETE": { + "TITLE": "Özel filtre sil", + "MESSAGE": "'{{customFilterName}}' özel filtresi" + } + }, + "TABLE": { + "COLUMNS": { + "TYPE": "Tip", + "SEVERITY": "Önem Derecesi", + "PRIORITY": "Öncelik", + "SUBJECT": "Konu", + "VOTES": "Oylar", + "STATUS": "Durum", + "CREATED": "Oluşturuldu", + "ASSIGNED_TO": "Atanmış" + }, + "TITLE_ACTION_CHANGE_STATUS": "Durumu değiştir", + "TITLE_ACTION_ASSIGNED_TO": "Atanmış", + "BLOCKED": "Engelli", + "EMPTY": { + "TITLE": "Bildirilen sorun yok :)", + "SUBTITLE": "Bir sorun mu buldunuz?" + } + } + }, + "ISSUE": { + "PAGE_TITLE": "{{issueSubject}} - Sorun {{issueRef}} - {{projectName}}", + "PAGE_DESCRIPTION": "Durum: {{issueStatus }}. Tip: {{issueType}}, Öncelik: {{issuePriority}}. Önem: {{issueSeverity}}. Tanı: {{issueDescription}}" + }, + "KANBAN": { + "PAGE_TITLE": "Kanban - {{projectName}}", + "PAGE_DESCRIPTION": "{{projectName}} projesinin kullanıcı hikayeleriyle birlikte kanban paneli: {{projectDescription}}", + "SECTION_NAME": "Kanban", + "TITLE_ACTION_FOLD": "Sütun Katla", + "TITLE_ACTION_UNFOLD": "Sütun katını aç", + "TITLE_ACTION_FOLD_CARDS": "Kartları katla", + "TITLE_ACTION_UNFOLD_CARDS": "Kartların katlarını aç", + "TITLE_ACTION_ADD_US": "Yeni Kullanıcı Hikayesi Ekle", + "TITLE_ACTION_ADD_BULK": "Yeni toplu ekle", + "ACTION_SHOW_ARCHIVED": "Arşivlenmişleri Göster", + "ACTION_HIDE_ARCHIVED": "Arşivlenmişleri gizle", + "HIDDEN_USER_STORIES": "Bu durumdaki kullanıcı hikayeleri otomatik olarak gizlenir", + "ARCHIVED": "Arşivlendiniz", + "UNDO_ARCHIVED": "Geri almak için tutun ve sürükleyin", + "PLACEHOLDER_CARD_TITLE": "Bunlar Kullanıcı Hikayeleriniz", + "PLACEHOLDER_CARD_TEXT": "Gereksinimleri ayrıştırmak için hikayeler alt görevlere sahip olabilirler" + }, + "SEARCH": { + "PAGE_TITLE": "Ara - {{projectName}}", + "PAGE_DESCRIPTION": "Projedeki hikayeleri, sorunları, işleri, viki sayfalarını ya da herhangi bir şeyi arayın {{projectName}}: {{projectDescription}}", + "FILTER_USER_STORIES": "Kullanıcı Hikayeleri", + "FILTER_ISSUES": "Sorunlar ", + "FILTER_TASKS": "Görevler", + "FILTER_WIKI": "Wiki Sayfaları", + "PLACEHOLDER_SEARCH": "Aranan konum...", + "TITLE_ACTION_SEARCH": "ara", + "EMPTY_TITLE": "Görünüşe göre arama kriterinize göre hiç bir şey bulunamadı", + "EMPTY_DESCRIPTION": "Üstteki sekmelerden birini deneyin ya da yeniden arayın" + }, + "TEAM": { + "PAGE_TITLE": "Takım - {{projectName}}", + "PAGE_DESCRIPTION": "Projedeki üyelerin tümünü göstermek için takım paneli {{projectName}}: {{projectDescription}}", + "SECTION_NAME": "Takım", + "APP_TITLE": "TAKIM - {{projectName}}", + "PLACEHOLDER_INPUT_SEARCH": "Tam ad ile ara", + "COLUMN_MR_WOLF": "Reyiz", + "EXPLANATION_COLUMN_MR_WOLF": "Kapatılmış sorunlar", + "COLUMN_IOCAINE": "Baldıran İçici", + "EXPLANATION_COLUMN_IOCAINE": "içilen baldıran miktarı", + "COLUMN_CERVANTES": "Katip", + "EXPLANATION_COLUMN_CERVANTES": "Düzenlenmiş Wiki sayfaları", + "COLUMN_BUG_HUNTER": "Böcek Avcısı", + "EXPLANATION_COLUMN_BUG_HUNTER": "Bildirilen sorunlar", + "COLUMN_NIGHT_SHIFT": "Akşamcı", + "EXPLANATION_COLUMN_NIGHT_SHIFT": "Kapatılmış görevler", + "COLUMN_TOTAL_POWER": "Toplam Güç", + "EXPLANATION_COLUMN_TOTAL_POWER": "Toplam Puanlar", + "SECTION_TITLE_TEAM": "Takım >", + "SECTION_FILTER_ALL": "Hepsi", + "CONFIRM_LEAVE_PROJECT": "Projeden ayrılmak istediğine emin misin?", + "ACTION_LEAVE_PROJECT": "Bu projeden ayrıl" + }, + "USER_SETTINGS": { + "AVATAR_MAX_SIZE": "[Max. boyut: {{maxFileSize}}]", + "MENU": { + "SECTION_TITLE": "Kullanıcı Ayarları", + "USER_PROFILE": "Kullanıcı profili", + "CHANGE_PASSWORD": "Parolayı değiştir", + "EMAIL_NOTIFICATIONS": "E-posta bildirimleri" + }, + "NOTIFICATIONS": { + "SECTION_NAME": "E-posta Bildirimleri", + "COLUMN_PROJECT": "Proje", + "COLUMN_RECEIVE_ALL": "Hepsini al", + "COLUMN_ONLY_INVOLVED": "Sadece İlgililer", + "COLUMN_NO_NOTIFICATIONS": "Bildirimler kapalı", + "OPTION_ALL": "Hepsi", + "OPTION_INVOLVED": "İlgili", + "OPTION_NONE": "Hiçbiri" + }, + "POPOVER": { + "USER_PROFILE": "Kullanıcı Profili", + "CHANGE_PASSWORD": "Parolayı Değiştir", + "NOTIFICATIONS": "Bildirimler", + "FEEDBACK": "Geribildirim", + "TITLE_AVATAR": "Kullanıcı tercihleri" + } + }, + "USER_PROFILE": { + "IMAGE_HELP": "Resim 80×80 piksel boyutuna ölçeklenecek.", + "ACTION_CHANGE_IMAGE": "Değiştir", + "ACTION_USE_GRAVATAR": "Varsayılan resmi kullan", + "ACTION_DELETE_ACCOUNT": "Taiga hesabını sil", + "CHANGE_EMAIL_SUCCESS": "Gelen kutunuzu kontrol edin!
Yeni adresinizi belirlemek için gerekli
yönergeleri içeren postayı gönderdik", + "CHANGE_PHOTO": "Resim değiştir", + "FIELD": { + "USERNAME": "Kullanıcı adı", + "EMAIL": "E-posta", + "FULL_NAME": "Tam isim", + "PLACEHOLDER_FULL_NAME": "Tam adınızı giriniz (ör. Bilge Tonyukuk)", + "BIO": "Bio (max. 210 karakter)", + "PLACEHOLDER_BIO": "Bize kendin hakkında bir şeyler söyle", + "LANGUAGE": "Dil", + "LANGUAGE_DEFAULT": "-- varsayılan dili kullan --", + "THEME": "Tema", + "THEME_DEFAULT": "-- varsayılan temayı kullan --" + } + }, + "WIZARD": { + "SECTION_TITLE_CHOOSE_TEMPLATE": "Bir şablon seç", + "CHOOSE_TEMPLATE_TEXT": "Hangi şablon sizin projeniz için daha uygun?", + "SECTION_TITLE_CREATE_PROJECT": "Proje Oluştur", + "CREATE_PROJECT_TEXT": "Taze ve temiz. Heycan verici!", + "PROGRESS_TEMPLATE_SELECTION": "Şablon seçimi", + "PROGRESS_NAME_DESCRIPTION": "İsim ve tanım" + }, + "WIKI": { + "PAGE_TITLE": "{{wikiPageName}} - Wiki - {{projectName}}", + "PAGE_DESCRIPTION": "Son sürüm {{lastModifiedDate}} ({{totalEditions}} toplamda sürüm sayısı) İçerik: {{ wikiPageContent }}", + "DATETIME": "DD MMM YYYY HH:mm", + "PLACEHOLDER_PAGE": "Wiki sayfanı yaz ", + "REMOVE": "Bu wiki sayfasını sil", + "DELETE_LIGHTBOX_TITLE": "Wiki Sayfası Sil", + "NAVIGATION": { + "SECTION_NAME": "Bağlantılar", + "ACTION_ADD_LINK": "Bağlantı ekle" + }, + "SUMMARY": { + "TIMES_EDITED": "kere
düzenlendi", + "LAST_EDIT": "son
düzenleme", + "LAST_MODIFICATION": "son düzenleme" + } + }, + "HINTS": { + "SECTION_NAME": "İpucu", + "LINK": "Nasıl kullanılacağını öğrenmek isterseniz destek sayfamıza bekleriz", + "LINK_TITLE": "Destek sayfamızı ziyaret edin", + "HINT1_TITLE": "Projeleri içe ya da dışarı aktarabileceğinizi biliyor muydunuz?", + "HINT1_TEXT": "Bu bir Taiga da yer alan tüm verilerinizi çıkararak diğerine taşımanıza olanak sağlar.", + "HINT2_TITLE": "Özel alanlar oluşturabileceğinizi biliyor muydunuz?", + "HINT2_TEXT": "Takımlar artık kendi iş akışlarına özgü bilgileri girebilecekleri özel alanlar oluşturabilirler", + "HINT3_TITLE": "Projelerinizi, sizi en çok ilgilendirenleri öne çıkartacak şekilde sıralayın.", + "HINT3_TEXT": "10 proje üstteki doğrudan erişim bölümünde gözükür", + "HINT4_TITLE": "Nerede kaldığınızı unuttunuz mu?", + "HINT4_TEXT": "Kaygılanmayın, çalışma sıranıza göre ayarlanan açık işlerinize, sorunlarınıza ve kullanıcı hikayelerinize panonuzdan erişebilirsiniz." + }, + "TIMELINE": { + "UPLOAD_ATTACHMENT": "{{username}} kullanıcısı {{obj_name}} 'ye bir yeni ek yükledi", + "US_CREATED": "{{project_name}} projesinde yer alacak {{us_name}} adlı yeni bir KH, {{username}} tarafından oluşturuldu", + "ISSUE_CREATED": "{{username}}, {{project_name}} projesinde yeni sorun ({{obj_name}}) oluşturdu", + "TASK_CREATED": "{{project_name}} projesindeki yeni görev {{obj_name}}, {{username}} tarafından oluşturuldu", + "TASK_CREATED_WITH_US": " {{project_name}} projesinde yer alan {{us_name}} adlı KH ya ait yeni bir görev {{obj_name}}, {{username}} tarafından oluşturuldu", + "WIKI_CREATED": "{{project_name}} projesindeki yeni wiki sayfası {{obj_name}}, {{username}} tarafından oluşturuldu", + "MILESTONE_CREATED": "{{project_name}} projesinde {{obj_name}} koşusu, {{username}} tarafından oluşturuldu", + "NEW_PROJECT": "{{project_name}} proje {{username}} tarafından oluşturuldu", + "MILESTONE_UPDATED": " {{obj_name}} koşusu {{username}} tarafından güncellendi", + "US_UPDATED": "{{username}} kullanıcısı, {{obj_name}} KH 'sinin \"{{field_name}}\" alanını güncelledi. ", + "US_UPDATED_WITH_NEW_VALUE": "{{username}}, {{obj_name}} hikayesinin {{field_name}} özelliğini {{new_value}} olacak şekilde değiştirdi", + "US_UPDATED_POINTS": "{{username}}, {{obj_name}} hikayesinin {{role_name}} puanını {{new_value}} yaptı", + "ISSUE_UPDATED": "{{obj_name}} talebinin \"{{field_name}}\" özniteliği {{username}} tarafından güncellendi. ", + "ISSUE_UPDATED_WITH_NEW_VALUE": "{{username}}, {{obj_name}} sorununun {{field_name}} özelliğini {{new_value}} olacak şekilde değiştirdi", + "TASK_UPDATED": "{{username}}, {{obj_name}} işinin {{field_name}} özelliğini {{new_value}} olacak şekilde değiştirdi", + "TASK_UPDATED_WITH_NEW_VALUE": "{{username}}, {{obj_name}} işinin {{field_name}} özelliğini {{new_value}} olacak şekilde değiştirdi", + "TASK_UPDATED_WITH_US": "{{us_name}} adlı KH'ye ait {{obj_name}} talebinin \"{{field_name}}\" özniteliği {{username}} tarafından güncellendi. ", + "TASK_UPDATED_WITH_US_NEW_VALUE": "{{username}}, {{us_name}} hikayesindeki {{obj_name}} işinin {{field_name}} özelliğini {{new_value}} olacak şekilde değiştirdi", + "WIKI_UPDATED": "{{obj_name}} adlı wiki sayfası {{username}} tarafından güncellendi", + "NEW_COMMENT_US": " {{obj_name}} KH'sine {{username}} tarafından yorum yapıldı", + "NEW_COMMENT_ISSUE": " {{obj_name}} talebine {{username}} tarafından yorum yapıldı", + "NEW_COMMENT_TASK": " {{obj_name}} görevine {{username}} tarafından yorum yapıldı", + "NEW_MEMBER": "{{project_name}} projesi yeni bir üyeye sahip", + "US_ADDED_MILESTONE": " {{username}}, {{sprint_name}} koşusuna {{obj_name}} hikayesini ekledi", + "US_MOVED": "{{username}}, {{obj_name}} hikayesini taşıdı", + "US_REMOVED_FROM_MILESTONE": "{{username}} havuza {{obj_name}} hikayesini ekledi", + "BLOCKED": "{{obj_name}}, {{username}} tarafından engellendi", + "UNBLOCKED": "{{obj_name}} nin engeli {{username}} tarafından kaldırıldı ", + "NEW_USER": "{{username}} Taiga ya katıldı" + }, + "LEGAL": { + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "Yeni bir hesap açarak
hizmet koşullarımızı ve gizlilik koşullarımızı kabul etmiş olursunuz." + }, + "EXTERNAL_APP": { + "PAGE_TITLE": "Bir dış uygulama oturum açmayı gerektiriyor", + "PAGE_DESCRIPTION": "Bir dış uygulama oturum açmayı gerektiriyor", + "AUTHORIZATION_REQUEST": "Taiga hesabınızı kullanması için {{application}} uygulamasına izin verin?", + "LOGIN_WITH_ANOTHER_USER": "Başka kullanıcı ile oturum açın", + "AUTHORIZE_APP": "Uygulamayı yetkilendir", + "CANCEL": "İptal" + }, + "JOYRIDE": { + "NAV": { + "NEXT": "İleri", + "BACK": "Arka", + "SKIP": "Geç", + "DONE": "Bitmiş" + }, + "DASHBOARD": { + "STEP1": { + "TITLE": "Projeniz", + "TEXT": "Hoşgeldiniz! Burada dahil olduğunuz projeleri göreceksiniz." + }, + "STEP2": { + "TITLE": "Üzerinde çalışılıyor", + "TEXT": "Burada üzerinde çalıştığınız hikayeleri, işleri ve sorunları göreceksiniz." + }, + "STEP3": { + "TITLE": "İzliyor", + "TEXT1": "Ve burada da projenizde bilmek isteyebileceklerinizi bulacaksınız.", + "TEXT2": "Zaten Taiga ile birlikte çalışmaktasınız ;)" + }, + "STEP4": { + "TITLE": "Hadi başlayın", + "TEXT1": "İlk Taiga projenizi oluşturarak başlayabilirsiniz.", + "TEXT2": "İyi şanslar!" + } + }, + "BACKLOG": { + "STEP1": { + "TITLE": "Proje özeti", + "TEXT1": "Buradan projenizin durumunu görebilirsiniz.", + "TEXT2": "Yönetim bölümünde ile her türlü proje ayarlarını değiştirebilirsiniz." + }, + "STEP2": { + "TITLE": "Ürün havuzu", + "TEXT": "Havuz, proje gereksinimlerinin (Kullanıcı Hikayeleri) sıralı listesidir. Koşularınızı planlayacağınız yer burasıdır." + }, + "STEP3": { + "TITLE": "Koşular", + "TEXT": "Koşular, belirlenmiş bir iş yükünün bitirilip teslim edilmesi beklenen tanımlı, kısa (genellikle 2'şer haftalık) zaman dilimleridir." + }, + "STEP4": { + "TITLE": "Kullanıcı Hikayeleri ", + "TEXT": "Bunlar üst seviye gereksinimler. Bunları önce havuza ekleyip sonra da teslim edilmeleri gereken koşuya çekebilirsiniz." + } + }, + "KANBAN": { + "STEP1": { + "TITLE": "İş akışınızı düzenleyin", + "TEXT": "İş akışı adımlarınıza eşleşen sütunları yönetim bölümünden ayarlayın." + }, + "STEP2": { + "TITLE": "Kullanıcı Hikayeleri & Görevleri", + "TEXT": "Kullanıcı hikayeleri üst düzey gereksinimlerdir. Onları farklı kolonlara taşıyabilirsiniz." + }, + "STEP3": { + "TITLE": "Kullanıcı hikayesi eklemek", + "TEXT1": "Kullanıcı Hikayelerini tek tek (KH ekle ikonu) veya toptan (toptan ikonu) girmek isteyebilirsiniz", + "TEXT2": "İyi şanslar!" + } + } + }, + "DISCOVER": { + "DISCOVER_TITLE": "Proje keşfedin", + "DISCOVER_SUBTITLE": "{projects, plural, one{Keşfedilecek bir açık proje} other{Keşfedilecek # açık proje}}", + "MOST_ACTIVE": "En hareketli", + "MOST_ACTIVE_EMPTY": "Henüz AKTİF proje yok", + "MOST_LIKED": "En beğenilen", + "MOST_LIKED_EMPTY": "Henüz BEĞENİLEN proje yok", + "VIEW_MORE": "Daha fazlası", + "RECRUITING": "Bu proje ahalisini arıyor", + "FEATURED": "Önemli projeler", + "EMPTY": "Bu arama ölçütlerine göre gösterilebilecek bir proje çıkmadı.
Tekrar deneyin!", + "FILTERS": { + "ALL": "Hepsi", + "KANBAN": "Kanban", + "SCRUM": "Scrum", + "PEOPLE": "İnsan arıyor", + "WEEK": "Geçen hafta", + "MONTH": "Geçen ay", + "YEAR": "Geçen yıl", + "ALL_TIME": "Tüm zamanlar", + "CLEAR": "Filtreleri kaldır" + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "Birşeyler yazın...", + "ACTION_TITLE": "Ara", + "RESULTS": "Arama sonuçları" + } + } +} \ No newline at end of file diff --git a/app/locales/taiga/locale-zh-hant.json b/app/locales/taiga/locale-zh-hant.json index f668a33f..8b3e88be 100644 --- a/app/locales/taiga/locale-zh-hant.json +++ b/app/locales/taiga/locale-zh-hant.json @@ -2,6 +2,7 @@ "COMMON": { "YES": "Yes", "NO": "No", + "OR": "or", "LOADING": "載入中...", "LOADING_PROJECT": "載入專案中...", "DATE": "DD MMM YYYY", @@ -18,14 +19,16 @@ "TAG_LINE": "你的敏捷、免費、和開放原始碼的專案管理工具", "TAG_LINE_2": "愛你的專案", "BLOCK": "封鎖", - "UNBLOCK": "解除封鎖", + "BLOCK_TITLE": "Block this item for example if it has a dependency that can not be satisfied", "BLOCKED": "已封鎖", + "UNBLOCK": "解除封鎖", + "UNBLOCK_TITLE": "解除項目封鎖", + "BLOCKED_NOTE": "為何遭到封鎖?", + "BLOCKED_REASON": "請解釋此原因", "CREATED_BY": "由 {{fullDisplayName}}創建", "FROM": "from", "TO": "to", "CLOSE": "關閉", - "BLOCKED_NOTE": "為何這個使用者故事被封鎖?", - "BLOCKED_REASON": "請解釋此原因", "GO_HOME": "帶我回到首頁", "PLUGINS": "外掛", "BETA": "我們在測試中", @@ -36,7 +39,9 @@ "EXTERNAL_USER": "外部使用者", "GENERIC_ERROR": "我們的系統指出{{error}}.", "IOCAINE_TEXT": "感到任務的不堪負荷?確認其它人在編輯任務時知道此狀況,可以點選Iocaine。它可能會成為免疫的致命狀況,只要長期小量消耗,但如果你只是偶而挑戰它可有助表現。", - "CAPSLOCK_WARNING": "Be careful! You're writing with capital letters and this input is case sensitive.", + "CLIENT_REQUIREMENT": "Client requirement is new requirement that was not previously expected and it is required to be part of the project", + "TEAM_REQUIREMENT": "Team requirement is a requirement that must exist in the project but should have no cost for the client", + "CAPSLOCK_WARNING": "注意你輸入了大寫符號,大小寫字母有所不同", "FORM_ERRORS": { "DEFAULT_MESSAGE": "該數值似乎為無效", "TYPE_EMAIL": "該電子郵件應為有效地址", @@ -139,35 +144,38 @@ }, "ASSIGNED_TO": { "NOT_ASSIGNED": "未指派", + "ASSIGN": "指派", "DELETE_ASSIGNMENT": "刪除指派", "REMOVE_ASSIGNED": "數值", "TOO_MANY": "太多用戶,繼續過濾中", "CONFIRM_UNASSIGNED": "你確定要讓它無任何指派嗎?", - "TITLE_ACTION_EDIT_ASSIGNMENT": "編輯指派" + "TITLE_ACTION_EDIT_ASSIGNMENT": "編輯指派", + "SELF": "指派給我" }, "STATUS": { "CLOSED": "關閉", "OPEN": "開啟" }, "WATCHERS": { - "ADD": "Add watchers", - "TITLE_ADD": "Add a project member to the watchers list", + "WATCHERS": "觀注者", + "ADD": "新增觀注者", + "TITLE_ADD": "將專案成員加到觀注者清單裏", "DELETE": "已刪除監督者", "TITLE_LIGHTBOX_DELETE_WARTCHER": "刪除監督者" }, "WATCH_BUTTON": { - "WATCH": "Watch", + "WATCH": "觀注", "WATCHING": "觀看中", - "UNWATCH": "Unwatch", - "WATCHERS": "Watchers", + "UNWATCH": "不觀注", + "WATCHERS": "觀注者", "BUTTON_TITLE": "Watch/Unwatch this item", - "COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}" + "COUNTER_TITLE": "{total, plural, one{一個觀注者} other{# 觀注者}}" }, "VOTE_BUTTON": { "UPVOTE": "Upvote", "UPVOTED": "Upvoted", "DOWNVOTE": "Downvote", - "VOTERS": "Voters", + "VOTERS": "投票者", "BUTTON_TITLE": "Upvote/Downvote this item", "COUNTER_TITLE": "{total, plural, one{one vote} other{# votes}}" }, @@ -176,7 +184,7 @@ "SAVE": "儲存客製化欄位", "EDIT": "編輯客製化欄位", "DELETE": "刪除客製屬性", - "CONFIRM_DELETE": "記得這個客制欄位的數值都將被刪除
你確定要繼續嗎" + "CONFIRM_DELETE": "記住在此客制欄位裏的數值資料都將遭刪除\n你確定要繼續嗎?" }, "FILTERS": { "TITLE": "過濾器", @@ -274,7 +282,7 @@ "HEADER": "我已登入Taiga", "PLACEHOLDER_AUTH_NAME": "使用者名稱或電子郵件(注意大小寫)", "LINK_FORGOT_PASSWORD": "忘記了?", - "TITLE_LINK_FORGOT_PASSWORD": "你忘了密碼嗎", + "TITLE_LINK_FORGOT_PASSWORD": "你是否忘了密碼嗎", "ACTION_ENTER": "Enter", "ACTION_SIGN_IN": "登入", "PLACEHOLDER_AUTH_PASSWORD": "密碼(大小寫有別)" @@ -336,7 +344,7 @@ "PAGE_DESCRIPTION": "接受邀請加入Taiga上的專案,它是一個給新創團隊與敏捷開發者設計師使用的專案管理平台。Taiga是一個簡易輕鬆美觀的工具,讓工作變成樂趣。" }, "INVITATION_LOGIN_FORM": { - "NOT_FOUND": "Our Oompa Loompas can't find your invitation.", + "NOT_FOUND": "我們找不到你的邀請碼", "SUCCESS": "你成功地加入此專案,歡迎來到 {{project_name}}", "ERROR": "按我們的記錄,你尚未註冊或是未輸入有效的密碼 " }, @@ -344,10 +352,11 @@ "PAGE_TITLE": "首頁 - Taiga", "PAGE_DESCRIPTION": "Taiga 首頁,你的主要專案以及任命,觀看使用者故事,任務與問題。", "EMPTY_WORKING_ON": "It feels empty, doesn't it? Start working with Taiga and you'll see here the stories, tasks and issues you are workin on.", - "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues... that you want to know about :)", + "EMPTY_WATCHING": "Follow User Stories, Tasks, Issues in your projects and be notified about its changes :)", "EMPTY_PROJECT_LIST": "你尚無任何專案", "WORKING_ON_SECTION": "進行中", - "WATCHING_SECTION": "觀看中" + "WATCHING_SECTION": "觀看中", + "DASHBOARD": "專案控制台" }, "PROJECTS": { "PAGE_TITLE": "我的專案 - Taiga", @@ -357,10 +366,13 @@ "ATTACHMENT": { "SECTION_NAME": "附件", "TITLE": "{{ fileName }} 上傳於 {{ date }}", + "LIST_VIEW_MODE": "列出檢視模視", + "GALLERY_VIEW_MODE": "檢視模視選項", "DESCRIPTION": "輸入一段簡短描述", "DEPRECATED": "(被棄用)", "DEPRECATED_FILE": "棄用?", "ADD": "加入新附件 {{maxFileSizeMsg}}", + "DROP": "在此移除附件 ", "MAX_FILE_SIZE": "[Max. size: {{maxFileSize}}]", "SHOW_DEPRECATED": "+ 顯示棄用的附件", "HIDE_DEPRECATED": "+ 隱藏棄用的附件", @@ -371,6 +383,7 @@ "TITLE_LIGHTBOX_DELETE_ATTACHMENT": "刪除附件....", "MSG_LIGHTBOX_DELETE_ATTACHMENT": "附件 '{{fileName}}'", "ERROR_DELETE_ATTACHMENT": "無法刪除: {{errorMessage}}", + "ERROR_MAX_SIZE_EXCEEDED": "'{{fileName}}' ({{fileSize}}) 超過系統容量上限, 請重傳小一點的檔案 ({{maxFileSize}})", "FIELDS": { "IS_DEPRECATED": "被棄用" } @@ -436,13 +449,22 @@ "PROJECT_DETAILS": "專案細節", "PROJECT_NAME": "專案名稱", "PROJECT_SLUG": "專案代稱", - "NUMBER_SPRINTS": "Number of sprints (0 for an undetermined quantity)", - "NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)", + "NUMBER_SPRINTS": "衝刺任務數目(0為未決定數量)", + "NUMBER_US_POINTS": "使用者故事點數數目 (0 為未決定數目)", "TAGS": "標籤", "DESCRIPTION": "描述", + "RECRUITING": "這個專案是否在徵人", + "RECRUITING_MESSAGE": "你在找誰?", + "RECRUITING_PLACEHOLDER": "定義你要找的資訊", "PUBLIC_PROJECT": "公開專案", + "PUBLIC_PROJECT_DESC": "用戶能夠找到並檢視你的專案", "PRIVATE_PROJECT": "不公開專案", - "DELETE": "刪除此專案" + "PRIVATE_PROJECT_DESC": "本專案預設為私密不公開顯示", + "PRIVATE_OR_PUBLIC": "公開專案和私密專案有何差異?", + "DELETE": "刪除此專案", + "LOGO_HELP": "此圖片將被裁成80x80px.
", + "CHANGE_LOGO": "更改圖標", + "ACTION_USE_DEFAULT_LOGO": "使用設預圖片" }, "REPORTS": { "TITLE": "Reports", @@ -473,7 +495,7 @@ "ISSUE_ADD": "在問題中加入客制欄位", "FIELD_TYPE_TEXT": "單行文字", "FIELD_TYPE_MULTI": "多行", - "FIELD_TYPE_DATE": "Date" + "FIELD_TYPE_DATE": "日期" }, "PROJECT_VALUES": { "PAGE_TITLE": "{{sectionName}} - 專案數值 - {{projectName}}", @@ -579,7 +601,7 @@ "CANCEL_TITLE": "取消創建", "SET_FIELD_NAME": "設定你的客制欄位名稱 ", "SET_FIELD_DESCRIPTION": "設定你客製化欄位的文字描述", - "FIELD_TYPE_DEFAULT": "-- select one --", + "FIELD_TYPE_DEFAULT": "-- 選擇其一 --", "ACTION_UPDATE": "更新客製化欄位", "ACTION_CANCEL_EDITION": "取消編輯 " }, @@ -591,7 +613,7 @@ "STATUS_ACTIVE": "活躍 ", "STATUS_PENDING": "待辦中", "DELETE_MEMBER": "刪除成員", - "RESEND": "Resend", + "RESEND": "重新送出", "SUCCESS_SEND_INVITATION": "我們已再次發出邀請信給'{{email}}'. ", "ERROR_SEND_INVITATION": "我們未送出邀請 ", "SUCCESS_DELETE": "已刪除 {{message}}.", @@ -665,38 +687,38 @@ "CURRENT_USER_CONTACTS_EMPTY_EXPLAIN": "你在Taiga一同工作的伙伴將自動成為你的聯絡人", "REPORT": "回報濫用", "TABS": { - "ACTIVITY_TAB": "動態", - "ACTIVITY_TAB_TITLE": "Show all the activity of this user", + "ACTIVITY_TAB": "時間表", + "ACTIVITY_TAB_TITLE": "顯示該用戶的所有活動", "PROJECTS_TAB": "專案", - "PROJECTS_TAB_TITLE": "List of all projects in which the user is a member", - "LIKES_TAB": "Likes", - "LIKES_TAB_TITLE": "List all likes made by this user", + "PROJECTS_TAB_TITLE": "列出所有專案的用戶成員名單", + "LIKES_TAB": "喜歡", + "LIKES_TAB_TITLE": "列出該使用者的喜受清單", "VOTES_TAB": "投票數", - "VOTES_TAB_TITLE": "List all votes made by this user", - "WATCHED_TAB": "Watched", - "WATCHED_TAB_TITLE": "List all item watched by this user", + "VOTES_TAB_TITLE": "列出該使用者的所有投票數", + "WATCHED_TAB": "已觀注", + "WATCHED_TAB_TITLE": "該使用者所有觀注的專案清單", "CONTACTS_TAB": "聯絡人", - "CONTACTS_TAB_TITLE": "List all contacts made by this user" + "CONTACTS_TAB_TITLE": "列出該使用者所有的聯絡人" } }, "PROFILE_SIDEBAR": { "TITLE": "個人資訊", "DESCRIPTION": "People can see everything you do and what you are working on. Add a nice bio to give an enhanced version of your information.", - "ADD_INFO": "Edit bio" + "ADD_INFO": "編輯自介" }, "PROFILE_FAVS": { - "FILTER_INPUT_PLACEHOLDER": "Type something...", + "FILTER_INPUT_PLACEHOLDER": "輸入文字", "FILTER_TYPE_ALL": "所有", - "FILTER_TYPE_ALL_TITLE": "Show all", + "FILTER_TYPE_ALL_TITLE": "顯示全部", "FILTER_TYPE_PROJECTS": "專案", - "FILTER_TYPE_PROJECT_TITLES": "Show only projects", - "FILTER_TYPE_USER_STORIES": "Stories", - "FILTER_TYPE_USER_STORIES_TITLES": "Show only user stories", + "FILTER_TYPE_PROJECT_TITLES": "只顯示專案", + "FILTER_TYPE_USER_STORIES": "故事", + "FILTER_TYPE_USER_STORIES_TITLES": "只顯視使用者故事", "FILTER_TYPE_TASKS": "任務 ", - "FILTER_TYPE_TASK_TITLES": "Show only tasks", + "FILTER_TYPE_TASK_TITLES": "只顯示任務 ", "FILTER_TYPE_ISSUES": "問題 ", - "FILTER_TYPE_ISSUES_TITLE": "Show only issues", - "EMPTY_TITLE": "It looks like there's nothing to show here." + "FILTER_TYPE_ISSUES_TITLE": "只顯示問題 ", + "EMPTY_TITLE": "似乎沒有東西可顯示" } }, "PROJECT": { @@ -705,6 +727,10 @@ "SECTION_PROJECTS": "專案", "HELP": "記錄你的專案在上方常用欄位
前十個專案將會顯示在上方導覽處。", "PRIVATE": "不公開專案", + "LOOKING_FOR_PEOPLE": "這個專案正在徵人", + "FANS_COUNTER_TITLE": "{total, plural, one{一個粉絲} other{# 粉絲}}", + "WATCHERS_COUNTER_TITLE": "{total, plural, one{一個觀注者} other{# 觀注者}}", + "MEMBERS_COUNTER_TITLE": "{total, plural, one{one member} other{# members}}", "STATS": { "PROJECT": "專案
點數", "DEFINED": "已定義
點數", @@ -727,13 +753,14 @@ "PLACEHOLDER_SEARCH": "搜尋", "ACTION_CREATE_PROJECT": "創建專案", "ACTION_IMPORT_PROJECT": "滙入專案", - "SEE_MORE_PROJECTS": "觀看更多專案", + "MANAGE_PROJECTS": "管理專案", "TITLE_CREATE_PROJECT": "創建專案", "TITLE_IMPORT_PROJECT": "滙入專案", "TITLE_PRVIOUS_PROJECT": "顯示過去專案", "TITLE_NEXT_PROJECT": "顯示下一個任務", "HELP_TITLE": "Taiga支援頁", "HELP": "幫助", + "HOMEPAGE": "首頁", "FEEDBACK_TITLE": "送出回饋 ", "FEEDBACK": "回饋 ", "NOTIFICATIONS_TITLE": "編輯個人通知設定", @@ -767,24 +794,24 @@ "SYNC_SUCCESS": "你的專案已成功滙入" }, "LIKE_BUTTON": { - "LIKE": "Like", - "LIKED": "Liked", - "UNLIKE": "Unlike", - "BUTTON_TITLE": "Like or unlike this project", - "COUNTER_TITLE": "{total, plural, one{one fan} other{# fans}}" + "LIKE": "喜歡", + "LIKED": "喜歡", + "UNLIKE": "不喜歡", + "BUTTON_TITLE": "是否喜歡該專案", + "COUNTER_TITLE": "{total, plural, one{一個粉絲} other{# 粉絲}}" }, "WATCH_BUTTON": { - "BUTTON_TITLE": "Watch this project and set notification policy", - "WATCH": "Watch", + "BUTTON_TITLE": "觀注本專案並設置通知選項", + "WATCH": "觀注", "WATCHING": "觀看中", - "COUNTER_TITLE": "{total, plural, one{one watcher} other{# watchers}}", + "COUNTER_TITLE": "{total, plural, one{一個觀注者} other{# 觀注者}}", "OPTIONS": { - "NOTIFY_ALL": "Receive all notifications", - "NOTIFY_ALL_TITLE": "Receive all notifications for this project", - "NOTIFY_INVOLVED": "Only involved", - "NOTIFY_INVOLVED_TITLE": "Recive notificacions only when you are involved", - "UNWATCH": "Unwatch", - "UNWATCH_TITLE": "Unwatch this project" + "NOTIFY_ALL": "接收所有通知", + "NOTIFY_ALL_TITLE": "接到該專案的所有通知", + "NOTIFY_INVOLVED": "只有相關", + "NOTIFY_INVOLVED_TITLE": "只接收與你相關的通知訊息", + "UNWATCH": "不觀注", + "UNWATCH_TITLE": "不再觀注此專案" } } }, @@ -858,7 +885,7 @@ "SECTION_NAME": "使用者故事細節", "LINK_TASKBOARD": "任務板", "TITLE_LINK_TASKBOARD": "到任務板去", - "TOTAL_POINTS": "total points", + "TOTAL_POINTS": "所有點數", "ADD": "+新增使用者故事", "ADD_BULK": "批次加入新使用者故事", "PROMOTED": "此使用者故事已提昇成問題:", @@ -887,6 +914,7 @@ "TYPE_NEW_COMMENT": "在此輸入一個新的評論", "SHOW_DELETED": "顯示遭刪除的評論 ", "HIDE_DELETED": "隱藏已刪除之評論 ", + "DELETE": "刪除評論 ", "RESTORE": "恢復原評論 " }, "ACTIVITY": { @@ -948,8 +976,9 @@ "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": "管理者", - "CUSTOMIZE_GRAPH_TITLE": "Set up the points and sprints through the Admin", + "CUSTOMIZE_GRAPH_TITLE": "透由管理頁面設置點數與衝刺任務 \n ", "MOVE_US_TO_CURRENT_SPRINT": "移到目前的 Sprint", + "MOVE_US_TO_LATEST_SPRINT": "移到最近的一個衝刺任務 ", "SHOW_FILTERS": "顯示過濾器", "SHOW_TAGS": "顯示標籤 (Tag)", "EMPTY": "The backlog is empty!", @@ -1012,10 +1041,11 @@ "LINK_TASKBOARD": "衝刺任務板", "TITLE_LINK_TASKBOARD": "到任務板 {{spring.name}}", "NUMBER_SPRINTS": "
衝刺任務 ", - "EMPTY": "There are no sprints yet", + "EMPTY": "尚無衝刺任務", + "WARNING_EMPTY_SPRINT_ANONYMOUS": "此衝刺任務無使用者故事", "WARNING_EMPTY_SPRINT": "Drop here Stories from your backlog to start a new sprint", - "TITLE_ACTION_NEW_SPRINT": "Add new sprint", - "TEXT_ACTION_NEW_SPRINT": "You may want to create a new sprint in your project", + "TITLE_ACTION_NEW_SPRINT": "增加衝刺任務", + "TEXT_ACTION_NEW_SPRINT": "你可以在你的專案下創建一個新衝刺任務", "ACTION_SHOW_CLOSED_SPRINTS": "顯示關閉衝刺任務", "ACTION_HIDE_CLOSED_SPRINTS": "隱藏衝刺任務" } @@ -1025,7 +1055,7 @@ "NOT_FOUND": "找不到", "NOT_FOUND_TEXT": "Error 404,你要找的網頁不存在,你可以稍後再回來TAIGA首頁,看看是否能再次找到要找的東西。", "PERMISSION_DENIED": "無此權限", - "PERMISSION_DENIED_TEXT": "You don't have permission to access to this page.", + "PERMISSION_DENIED_TEXT": "您無權限設進入此頁面", "VERSION_ERROR": "Taiga某人之前更改了這個,而我們的工程師無法再做改變。請重新載入頁面來使用你的更新(之前設定將消失)" }, "TASKBOARD": { @@ -1036,7 +1066,7 @@ "TITLE_ACTION_ADD_BULK": "批次加入新任務 ", "TITLE_ACTION_ASSIGN": "指派任務 ", "TITLE_ACTION_EDIT": "結束任務 ", - "PLACEHOLDER_CARD_TITLE": "This could be a task", + "PLACEHOLDER_CARD_TITLE": "這可能是一項任務", "PLACEHOLDER_CARD_TEXT": "Split Stories into tasks to track them separately", "TABLE": { "COLUMN": "使用者故事", @@ -1189,8 +1219,8 @@ "HIDDEN_USER_STORIES": "此狀態下的使用者故事預設為隱藏", "ARCHIVED": "你已歸檔", "UNDO_ARCHIVED": "拖移 & 丟到未做", - "PLACEHOLDER_CARD_TITLE": "These are your User Stories", - "PLACEHOLDER_CARD_TEXT": "Stories might also have subtasks to separate requirements" + "PLACEHOLDER_CARD_TITLE": "這裏是你的使用者故事", + "PLACEHOLDER_CARD_TEXT": "在此可以找到你想了解的專案" }, "SEARCH": { "PAGE_TITLE": "搜尋 - {{projectName}}", @@ -1256,7 +1286,7 @@ "USER_PROFILE": { "IMAGE_HELP": "此圖片將被裁成80x80px.
", "ACTION_CHANGE_IMAGE": "變更", - "ACTION_USE_GRAVATAR": "用戶大頭貼圖片", + "ACTION_USE_GRAVATAR": "使用預設圖像", "ACTION_DELETE_ACCOUNT": "刪除Taiga帳戶", "CHANGE_EMAIL_SUCCESS": "檢查你的收信箱
我們送出了一封信
裏面有設定你新電子郵件的相關指示", "CHANGE_PHOTO": "變更照片", @@ -1305,10 +1335,10 @@ "HINT1_TITLE": "你知道可以滙入與滙出專案嗎?", "HINT1_TEXT": "可以從 Taiga提取所有個人資料記錄,移入其它地方。", "HINT2_TITLE": "你知道可以創建個人化欄位嗎", - "HINT2_TEXT": "Teams can now create custom fields as a flexible means to enter specific data useful for their particular workflow.", - "HINT3_TITLE": "Reorder your projects to feature those most relevant to you.", - "HINT3_TEXT": "The 10 projects are listed in the direct access bar at the top.", - "HINT4_TITLE": "你是否曾忘了自己要做什麼?", + "HINT2_TEXT": "團隊可以使用客制化欄位來彈性地輸入特殊資料以應付其特定的工作流程 ", + "HINT3_TITLE": "重新整理與你相關的專案特徵", + "HINT3_TEXT": "本頁上方直接列出前10個專案選單", + "HINT4_TITLE": "你是否忘了你在做什麼?", "HINT4_TEXT": "別憺心,在控制台可以找到你的公開任務、問題以及使用者故事的工作次序。" }, "TIMELINE": { @@ -1336,49 +1366,55 @@ "NEW_COMMENT_TASK": "{{username}} 評論了此任務{{obj_name}}", "NEW_MEMBER": "{{project_name}} 有新成員", "US_ADDED_MILESTONE": "{{username}} 增加使用者故事 {{obj_name}} 給 {{sprint_name}}", - "US_MOVED": "{{username}} has moved the US {{obj_name}}", + "US_MOVED": "{{username}} 搬移了使用者故事 {{obj_name}}", "US_REMOVED_FROM_MILESTONE": "{{username}} 新增使用者故事 {{obj_name}} 到待辦工作優先列表", "BLOCKED": "{{username}} 封鎖了 {{obj_name}}", "UNBLOCKED": "{{username}} 解除了封鎖 {{obj_name}}", "NEW_USER": "{{username}} 已加入 Taiga" }, "LEGAL": { - "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "By clicking \"Sign up\"', you agree to our
terms of service and privacy policy." + "TERMS_OF_SERVICE_AND_PRIVACY_POLICY_AD": "當創建新帳號時,你必須同意我們的
服務條款隱私權政策." }, "EXTERNAL_APP": { - "PAGE_TITLE": "An external app requires authentication", - "PAGE_DESCRIPTION": "An external app requires authentication", - "AUTHORIZATION_REQUEST": "Authorize {{application}} to use your Taiga account?", - "LOGIN_WITH_ANOTHER_USER": "Login with another user", - "AUTHORIZE_APP": "Authorize app", + "PAGE_TITLE": "外部的應用程式要求取得授權", + "PAGE_DESCRIPTION": "外部的應用程式要求取得授權", + "AUTHORIZATION_REQUEST": "同意授權 {{application}} 應用程式使用你的帳戶資訊?", + "LOGIN_WITH_ANOTHER_USER": "以其它帳號登入", + "AUTHORIZE_APP": "授權的應用服務 ", "CANCEL": "取消" }, "JOYRIDE": { + "NAV": { + "NEXT": "下一個", + "BACK": "後台", + "SKIP": "略過", + "DONE": "完成" + }, "DASHBOARD": { "STEP1": { "TITLE": "你的專案", - "TEXT": "Welcome! Here you will find the projects you are involved on." + "TEXT": "歡迎!你可以在此找到相關的專案" }, "STEP2": { "TITLE": "進行中", - "TEXT": "Here you will find the User Stories, Tasks and Issues in which you are working on." + "TEXT": "你可以在此找到你正在處理的使用者故事,任務,問題。 " }, "STEP3": { "TITLE": "觀看中", - "TEXT1": "And right here you will find the ones that you want to know about.", - "TEXT2": "You are already working with Taiga ;)" + "TEXT1": "在此你可以找到你想知道的專案", + "TEXT2": "你現在可利用Taiga進行工作了" }, "STEP4": { - "TITLE": "Let’s start", - "TEXT1": "You can start by creating your first Taiga project.", - "TEXT2": "Good luck!" + "TITLE": "我們開始吧", + "TEXT1": "你可以開始創建你在Taiga上的第一個專案了", + "TEXT2": "祝好運!" } }, "BACKLOG": { "STEP1": { - "TITLE": "Project summary", - "TEXT1": "Here you will see the state of your project.", - "TEXT2": "You can change every kind of project settings through the admin." + "TITLE": "專案摘要", + "TEXT1": "你可以看到專案的狀態", + "TEXT2": "你可以在管理頁面變更專案的設定" }, "STEP2": { "TITLE": "Product backlog", @@ -1386,7 +1422,7 @@ }, "STEP3": { "TITLE": "衝刺任務", - "TEXT": "Sprints are short periods of time (usually 2 weeks) during which specific work has to be completed and delivered." + "TEXT": "衝刺任務為短期(通常為2 週)內特定工作必須要完成與送達." }, "STEP4": { "TITLE": "使用者故事", @@ -1395,18 +1431,46 @@ }, "KANBAN": { "STEP1": { - "TITLE": "Customize your workflow", - "TEXT": "Set up the columns you need to map your workflow statuses through the admin." + "TITLE": "調整你的工作流程 ", + "TEXT": "你可在管理者頁面設置欄位來繪製工作流程狀態" }, "STEP2": { - "TITLE": "User Stories & Tasks", + "TITLE": "使用者故事與任務", "TEXT": "User Stories are the requirements at high level. You can drag them to different columns." }, "STEP3": { - "TITLE": "Adding User Stories", + "TITLE": "新增使用者故事", "TEXT1": "You may want to add a single User Story (add US icon) or a group of them (bulk icon)", - "TEXT2": "Good luck!" + "TEXT2": "祝好運!" } } + }, + "DISCOVER": { + "DISCOVER_TITLE": "發現更多專案", + "DISCOVER_SUBTITLE": "{projects, plural, one{One public project to discover} other{# public projects to discover}}", + "MOST_ACTIVE": "最活躍", + "MOST_ACTIVE_EMPTY": "目前尚無活進行中的專案", + "MOST_LIKED": "最受喜愛的", + "MOST_LIKED_EMPTY": "尚未有喜愛的專案", + "VIEW_MORE": "顯示更多", + "RECRUITING": "這個專案正在徵人", + "FEATURED": "受矚目專案", + "EMPTY": "沒有專案符合你尋找的條件.
再試一次", + "FILTERS": { + "ALL": "所有", + "KANBAN": "Kanban(看板)", + "SCRUM": "Scrum", + "PEOPLE": "找尋人員", + "WEEK": "上星期", + "MONTH": "上個月", + "YEAR": "去年", + "ALL_TIME": "全部時間", + "CLEAR": "清除過濾器 " + }, + "SEARCH": { + "INPUT_PLACEHOLDER": "輸入文字", + "ACTION_TITLE": "搜尋", + "RESULTS": "搜尋結果" + } } } \ No newline at end of file diff --git a/app/modules/attachments/attachment-gallery.scss b/app/modules/attachments/attachment-gallery.scss new file mode 100644 index 00000000..029b29a9 --- /dev/null +++ b/app/modules/attachments/attachment-gallery.scss @@ -0,0 +1,70 @@ +.attachment-gallery { + display: flex; + flex-basis: 25%; + flex-wrap: wrap; + justify-content: flex-start; + margin-top: 1rem; + .single-attachment { + margin-bottom: .5rem; + margin-right: .5rem; + max-width: 200px; + &:hover { + .icon-delete { + opacity: 1; + } + } + .attachment-image { + display: inline-block; + } + img { + height: 150px; + margin-bottom: .2rem; + width: 200px; + &:hover { + filter: saturate(150%) hue-rotate(60deg); + transition: all .3s cubic-bezier(.01, .7, 1, 1); + } + } + &.deprecated { + img { + opacity: .5; + } + .attachment-name { + color: $gray-light; + } + } + .attachment-data { + align-content: center; + display: flex; + justify-content: space-between; + } + .attachment-name { + @extend %light; + @include ellipsis(175px); + display: inline-block; + } + .icon-delete { + color: $red-light; + margin-left: auto; + opacity: 0; + transition: opacity .3s ease-in; + transition-delay: .2s; + &:hover { + color: $red; + } + } + .loading-container { + align-items: center; + display: flex; + height: 150px; + justify-content: center; + margin: 0 .5rem .5rem 0; + width: 200px; + } + .loading-spinner { + margin: 0 auto; + max-height: 3rem; + max-width: 3rem; + } + } +} diff --git a/app/modules/attachments/attachment-list.scss b/app/modules/attachments/attachment-list.scss new file mode 100644 index 00000000..dc2e21db --- /dev/null +++ b/app/modules/attachments/attachment-list.scss @@ -0,0 +1,103 @@ +.attachment-list { + .single-attachment { + align-items: center; + border-bottom: 1px solid $whitish; + display: flex; + padding: .5rem 0 .5rem .5rem; + position: relative; + &:hover { + .settings { + opacity: 1; + transition: opacity .2s ease-in; + } + } + &.deprecated { + color: $gray-light; + .attachment-name a { + color: $gray-light; + } + } + } + .attachment-name { + @include ellipsis(90%); + flex-basis: 25%; + flex-grow: 1; + flex-shrink: 0; + padding-right: 1rem; + } + .attachment-comments, + .editable-attachment-comment { + flex: 2; + flex-basis: 50%; + margin-right: .5rem; + span { + color: $gray; + } + } + .attachment-size { + flex-basis: 125px; + flex-grow: 0; + flex-shrink: 0; + } + .attachment-settings { + align-items: center; + display: flex; + flex-basis: 10%; + flex-grow: 0; + flex-shrink: 0; + justify-content: space-around; + margin-left: auto; + .settings, + .editable-settings { + @extend %large; + color: $gray-light; + &:hover { + color: $primary; + } + } + .settings { + opacity: 0; + } + .editable-settings { + opacity: 1; + } + .icon-delete { + &:hover { + color: $red; + } + } + .icon-drag-v { + cursor: move; + } + } + .icon-delete { + @extend %large; + color: $gray-light; + &:hover { + color: $red; + } + } + .editable-attachment-deprecated { + display: flex; + padding-left: 1rem; + span { + color: $gray-light; + } + input { + margin-right: .2rem; + vertical-align: middle; + &:checked+span { + color: $grayer; + } + } + } + .percentage { + background: rgba($primary, .1); + bottom: 0; + height: 40px; + left: 0; + position: absolute; + top: 0; + width: 45%; + } +} diff --git a/app/modules/attachments/attachments.scss b/app/modules/attachments/attachments.scss new file mode 100644 index 00000000..b110a0c4 --- /dev/null +++ b/app/modules/attachments/attachments.scss @@ -0,0 +1,122 @@ +.attachments { + margin-bottom: 2rem; +} + +.attachments-header { + align-content: space-between; + align-items: center; + background: $whitish; + display: flex; + justify-content: space-between; + min-height: 36px; + .attachments-title { + @extend %medium; + @extend %bold; + color: $grayer; + padding: 0 1rem; + } + .options { + display: flex; + } + label { + cursor: pointer; + margin-left: 1rem; + &.add-attachment-button { + background: $gray; + border: 0; + display: inline-block; + padding: .5rem; + transition: background .25s; + &:hover { + background: $primary-light; + } + } + svg { + fill: $white; + height: 1.25rem; + margin-bottom: -.2rem; + width: 1.25rem; + } + } + button { + background: none; + margin-right: .2rem; + &:hover, + &.is-active { + svg { + fill: $primary-light; + } + } + svg { + fill: $gray-light; + height: 1.6rem; + margin-bottom: -.2rem; + width: 1.6rem; + } + } + input { + display: none; + } +} + +.attachments-empty { + @extend %large; + @extend %bold; + border: 3px dashed $whitish; + color: $gray-light; + margin-top: .5rem; + padding: 1rem; + text-align: center; +} + +.single-attachment { + @extend %small; + &.ui-sortable-helper { + background: lighten($primary, 60%); + box-shadow: 1px 1px 10px rgba($black, .1); + transition: background .2s ease-in; + } + &.sortable-placeholder { + background: $whitish; + height: 40px; + } + .attachment-name { + @extend %bold; + padding-right: 1rem; + .icon { + margin-right: .25rem; + } + svg { + height: 1.2rem; + width: 1.2rem; + } + } + .attachment-size { + color: $gray-light; + } +} + +.more-attachments { + @extend %small; + border-bottom: 1px solid $gray-light; + display: block; + padding: 1rem 0 1rem 1rem; + span { + color: $gray-light; + } + .more-attachments-num { + color: $primary; + margin-left: .5rem; + } + &:hover { + background: lighten($primary, 60%); + transition: background .2s ease-in; + } +} + +.attachment-preview { + img { + max-height: 80vh; + max-width: 80vw; + } +} diff --git a/app/modules/components/attachment-link/attachment-link.directive.coffee b/app/modules/components/attachment-link/attachment-link.directive.coffee new file mode 100644 index 00000000..50cd109e --- /dev/null +++ b/app/modules/components/attachment-link/attachment-link.directive.coffee @@ -0,0 +1,45 @@ +### +# Copyright (C) 2014-2016 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: attachment-link.directive.coffee +### + +AttachmentLinkDirective = ($parse, lightboxFactory) -> + link = (scope, el, attrs) -> + attachment = $parse(attrs.tgAttachmentLink)(scope) + + el.on "click", (event) -> + if taiga.isImage(attachment.getIn(['file', 'name'])) + event.preventDefault() + + scope.$apply -> + lightboxFactory.create('tg-lb-attachment-preview', { + class: 'lightbox lightbox-block' + }, { + file: attachment.get('file') + }) + + scope.$on "$destroy", -> el.off() + return { + link: link + } + +AttachmentLinkDirective.$inject = [ + "$parse", + "tgLightboxFactory" +] + +angular.module("taigaComponents").directive("tgAttachmentLink", AttachmentLinkDirective) diff --git a/app/modules/components/attachment/attachment-gallery.directive.coffee b/app/modules/components/attachment/attachment-gallery.directive.coffee new file mode 100644 index 00000000..2214a893 --- /dev/null +++ b/app/modules/components/attachment/attachment-gallery.directive.coffee @@ -0,0 +1,39 @@ +### +# Copyright (C) 2014-2016 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: attachment-gallery.directive.coffee +### + +AttachmentGalleryDirective = () -> + link = (scope, el, attrs, ctrl) -> + + return { + scope: {}, + bindToController: { + attachment: "=", + onDelete: "&", + onUpdate: "&", + type: "=" + }, + controller: "Attachment", + controllerAs: "vm", + templateUrl: "components/attachment/attachment-gallery.html", + link: link + } + +AttachmentGalleryDirective.$inject = [] + +angular.module("taigaComponents").directive("tgAttachmentGallery", AttachmentGalleryDirective) diff --git a/app/modules/components/attachment/attachment-gallery.jade b/app/modules/components/attachment/attachment-gallery.jade new file mode 100644 index 00000000..dbb353b1 --- /dev/null +++ b/app/modules/components/attachment/attachment-gallery.jade @@ -0,0 +1,37 @@ +.single-attachment( + ng-class="{deprecated: vm.attachment.getIn(['file', 'is_deprecated'])}", + ng-if="vm.attachment.getIn(['file', 'id'])", +) + a.attachment-image( + tg-attachment-link="vm.attachment" + href="{{::vm.attachment.getIn(['file', 'url'])}}" + title="{{::vm.attachment.getIn(['file', 'name'])}}" + target="_blank" + download="{{::vm.attachment.getIn(['file', 'name'])}}" + ) + img( + alt="{{::vm.attachment.getIn(['file', 'name'])}}" + ng-src="{{::vm.attachment.getIn(['file', 'thumbnail_card_url'])}}" + ng-if="vm.attachment.getIn(['file', 'thumbnail_card_url'])" + ) + img( + alt="{{::vm.attachment.getIn(['file', 'name'])}}" + src="/#{v}/images/attachment-gallery.png" + ng-if="!vm.attachment.getIn(['file', 'thumbnail_card_url'])" + ) + .attachment-data + a.attachment-name( + tg-attachment-link="vm.attachment" + href="{{::vm.attachment.getIn(['file', 'url'])}}" + title="{{::vm.attachment.get(['file', 'name'])}}" + target="_blank" + download="{{::vm.attachment.getIn(['file', 'name'])}}" + ) + span {{::vm.attachment.getIn(['file', 'name'])}} + + a.icon-delete( + ng-if="vm.attachment.get('editable')" + href="" + title="{{'COMMON.DELETE' | translate}}" + ng-click="vm.delete()" + ) diff --git a/app/modules/components/attachment/attachment.controller.coffee b/app/modules/components/attachment/attachment.controller.coffee new file mode 100644 index 00000000..487f107e --- /dev/null +++ b/app/modules/components/attachment/attachment.controller.coffee @@ -0,0 +1,60 @@ +### +# Copyright (C) 2014-2015 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: attchment.controller.coffee +### + +class AttachmentController + @.$inject = [ + 'tgAttachmentsService', + '$translate' + ] + + constructor: (@attachmentsService, @translate) -> + @.form = {} + @.form.description = @.attachment.getIn(['file', 'description']) + @.form.is_deprecated = @.attachment.get(['file', 'is_deprecated']) + + @.title = @translate.instant("ATTACHMENT.TITLE", { + fileName: @.attachment.get('name'), + date: moment(@.attachment.get('created_date')).format(@translate.instant("ATTACHMENT.DATE")) + }) + + editMode: (mode) -> + attachment = @.attachment.set('editable', mode) + @.onUpdate({attachment: attachment}) + + delete: () -> + @.onDelete({attachment: @.attachment}) + + save: () -> + attachment = @.attachment.set('loading', true) + + @.onUpdate({attachment: attachment}) + + attachment = @.attachment.merge({ + editable: false, + loading: false + }) + + attachment = attachment.mergeIn(['file'], { + description: @.form.description, + is_deprecated: !!@.form.is_deprecated + }) + + @.onUpdate({attachment: attachment}) + +angular.module('taigaComponents').controller('Attachment', AttachmentController) diff --git a/app/modules/components/attachment/attachment.controller.spec.coffee b/app/modules/components/attachment/attachment.controller.spec.coffee new file mode 100644 index 00000000..2b7b70c8 --- /dev/null +++ b/app/modules/components/attachment/attachment.controller.spec.coffee @@ -0,0 +1,155 @@ +### +# Copyright (C) 2014-2015 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: attchment.controller.spec.coffee +### + +describe "AttachmentController", -> + $provide = null + $controller = null + scope = null + mocks = {} + + _mockAttachmentsService = -> + mocks.attachmentsService = {} + + $provide.value("tgAttachmentsService", mocks.attachmentsService) + + _mockTranslate = -> + mocks.translate = { + instant: sinon.stub() + } + + $provide.value("$translate", mocks.translate) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockAttachmentsService() + _mockTranslate() + + return null + + _inject = -> + inject (_$controller_, $rootScope) -> + $controller = _$controller_ + scope = $rootScope.$new() + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaComponents" + + _setup() + + it "change edit mode", () -> + attachment = Immutable.fromJS({ + file: { + description: 'desc', + is_deprecated: false + } + }) + + ctrl = $controller("Attachment", { + $scope: scope + }, { + attachment : attachment + }) + + ctrl.onUpdate = sinon.spy() + + onUpdate = sinon.match (value) -> + value = value.attachment.toJS() + + return value.editable + + , "onUpdate" + + ctrl.editMode(true) + + expect(ctrl.onUpdate).to.be.calledWith(onUpdate) + + it "delete", () -> + attachment = Immutable.fromJS({ + file: { + description: 'desc', + is_deprecated: false + } + }) + + ctrl = $controller("Attachment", { + $scope: scope + }, { + attachment : attachment + }) + + ctrl.onDelete = sinon.spy() + + onDelete = sinon.match (value) -> + return value.attachment == attachment + , "onDelete" + + ctrl.delete() + + expect(ctrl.onDelete).to.be.calledWith(onDelete) + + it "save", () -> + attachment = Immutable.fromJS({ + file: { + description: 'desc', + is_deprecated: false + }, + loading: false, + editable: false + }) + + ctrl = $controller("Attachment", { + $scope: scope + }, { + attachment : attachment + }) + + ctrl.onUpdate = sinon.spy() + + onUpdateLoading = sinon.match (value) -> + value = value.attachment.toJS() + + return value.loading + , "onUpdateLoading" + + onUpdate = sinon.match (value) -> + value = value.attachment.toJS() + + return ( + value.file.description == "ok" && + value.file.is_deprecated + ) + , "onUpdate" + + ctrl.form = { + description: "ok" + is_deprecated: true + } + + ctrl.save() + + attachment = ctrl.attachment.toJS() + + expect(ctrl.onUpdate).to.be.calledWith(onUpdateLoading) + expect(ctrl.onUpdate).to.be.calledWith(onUpdate) diff --git a/app/modules/components/attachment/attachment.directive.coffee b/app/modules/components/attachment/attachment.directive.coffee new file mode 100644 index 00000000..94202b8a --- /dev/null +++ b/app/modules/components/attachment/attachment.directive.coffee @@ -0,0 +1,39 @@ +### +# Copyright (C) 2014-2016 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: attachment.directive.coffee +### + +AttachmentDirective = () -> + link = (scope, el, attrs, ctrl) -> + + return { + scope: {}, + bindToController: { + attachment: "=", + onDelete: "&", + onUpdate: "&", + type: "=" + }, + controller: "Attachment", + controllerAs: "vm", + templateUrl: "components/attachment/attachment.html", + link: link + } + +AttachmentDirective.$inject = [] + +angular.module("taigaComponents").directive("tgAttachment", AttachmentDirective) diff --git a/app/modules/components/attachment/attachment.jade b/app/modules/components/attachment/attachment.jade new file mode 100644 index 00000000..32df1083 --- /dev/null +++ b/app/modules/components/attachment/attachment.jade @@ -0,0 +1,80 @@ +form.single-attachment( + ng-class="{deprecated: vm.attachment.getIn(['file', 'is_deprecated'])}", + ng-if="vm.attachment.getIn(['file', 'id'])", + ng-submit="vm.save()" +) + + .attachment-name + a( + tg-attachment-link="vm.attachment" + href="{{::vm.attachment.getIn(['file', 'url'])}}" + title="{{::vm.attachment.get(['file', 'name'])}}" + target="_blank" + download="{{::vm.attachment.getIn(['file', 'name'])}}" + ) + span.icon + include ../../../svg/attachment.svg + span {{::vm.attachment.getIn(['file', 'name'])}} + + .attachment-comments(ng-if="!vm.attachment.get('editable') && vm.attachment.getIn(['file', 'description'])") + span.deprecated-file(ng-if="vm.attachment.getIn(['file', 'is_deprecated'])") {{'ATTACHMENT.DEPRECATED' | translate}} + span {{vm.attachment.getIn(['file', 'description'])}} + + .attachment-size + span {{::vm.attachment.getIn(['file', 'size']) | sizeFormat}} + + .editable.editable-attachment-comment(ng-if="vm.attachment.get('editable')") + input( + type="text", + name="description", + maxlength="140", + ng-model="vm.form.description", + tg-auto-select, + ng-keydown="$event.which === 27 && vm.editMode(false)" + placeholder="{{'ATTACHMENT.DESCRIPTION' | translate}}" + ) + + .editable.editable-attachment-deprecated(ng-if="vm.attachment.get('editable')") + input( + type="checkbox" + ng-model="vm.form.is_deprecated" + name="is-deprecated" + id="attach-{{::vm.attachment.getIn(['file', 'id'])}}-is-deprecated" + ) + label.is_deprecated( + for="attach-{{::vm.attachment.getIn(['file', 'id'])}}-is-deprecated" + translate="{{'ATTACHMENT.DEPRECATED_FILE' | translate}}") + + .attachment-settings(ng-if="vm.attachment.get('editable')") + div(tg-loading="vm.attachment.get('loading')") + a.editable-settings.icon.icon-floppy( + href="" + title="{{'COMMON.SAVE' | translate}}" + ng-click="vm.save()" + ) + + div + a.editable-settings.icon.icon-delete( + href="" + title="{{'COMMON.CANCEL' | translate}}" + ng-click="vm.editMode(false)" + ) + + .attachment-settings( + ng-if="!vm.attachment.get('editable')" + tg-check-permission="modify_{{vm.type}}" + ) + a.settings.icon.icon-edit( + href="" + title="{{'COMMON.EDIT' | translate}}" + ng-click="vm.editMode(true)" + ) + a.settings.icon.icon-delete( + href="" + title="{{'COMMON.DELETE' | translate}}" + ng-click="vm.delete()" + ) + a.settings.icon.icon-drag-v( + href="" + title="{{'COMMON.DRAG' | translate}}" + ) diff --git a/app/modules/components/attachments-drop/attachments-drop.directive.coffee b/app/modules/components/attachments-drop/attachments-drop.directive.coffee new file mode 100644 index 00000000..7fb995a5 --- /dev/null +++ b/app/modules/components/attachments-drop/attachments-drop.directive.coffee @@ -0,0 +1,46 @@ +### +# Copyright (C) 2014-2015 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: attachments-drop.directive.coffee +### + +AttachmentsDropDirective = ($parse) -> + link = (scope, el, attrs) -> + eventAttr = $parse(attrs.tgAttachmentsDrop) + + el.on 'dragover', (e) -> + e.preventDefault() + return false + + el.on 'drop', (e) -> + e.stopPropagation() + e.preventDefault() + + dataTransfer = e.dataTransfer || (e.originalEvent && e.originalEvent.dataTransfer); + + scope.$apply () -> eventAttr(scope, {files: dataTransfer.files}) + + scope.$on "$destroy", -> el.off() + + return { + link: link + } + +AttachmentsDropDirective.$inject = [ + "$parse" +] + +angular.module("taigaComponents").directive("tgAttachmentsDrop", AttachmentsDropDirective) diff --git a/app/modules/components/attachments-full/attachments-full.controller.coffee b/app/modules/components/attachments-full/attachments-full.controller.coffee new file mode 100644 index 00000000..30055c87 --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.controller.coffee @@ -0,0 +1,88 @@ +### +# Copyright (C) 2014-2015 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: attchments-full.controller.coffee +### + +sizeFormat = @.taiga.sizeFormat + +class AttachmentsFullController + @.$inject = [ + "$translate", + "$tgConfirm", + "$tgConfig", + "$tgStorage", + "tgAttachmentsFullService" + ] + + constructor: (@translate, @confirm, @config, @storage, @attachmentsFullService) -> + @.mode = @storage.get('attachment-mode', 'list') + + @.maxFileSize = @config.get("maxUploadFileSize", null) + @.maxFileSize = sizeFormat(@.maxFileSize) if @.maxFileSize + @.maxFileSizeMsg = if @.maxFileSize then @translate.instant("ATTACHMENT.MAX_UPLOAD_SIZE", {maxFileSize: @.maxFileSize}) else "" + + taiga.defineImmutableProperty @, 'attachments', () => return @attachmentsFullService.attachments + taiga.defineImmutableProperty @, 'deprecatedsCount', () => return @attachmentsFullService.deprecatedsCount + taiga.defineImmutableProperty @, 'attachmentsVisible', () => return @attachmentsFullService.attachmentsVisible + taiga.defineImmutableProperty @, 'deprecatedsVisible', () => return @attachmentsFullService.deprecatedsVisible + + uploadingAttachments: () -> + return @attachmentsFullService.uploadingAttachments + + addAttachment: (file) -> + editable = (@.mode == 'list') + + @attachmentsFullService.addAttachment(@.projectId, @.objId, @.type, file, editable) + + setMode: (mode) -> + @.mode = mode + + @storage.set('attachment-mode', mode) + + toggleDeprecatedsVisible: () -> + @attachmentsFullService.toggleDeprecatedsVisible() + + addAttachments: (files) -> + _.forEach files, (file) => @.addAttachment(file) + + loadAttachments: -> + @attachmentsFullService.loadAttachments(@.type, @.objId, @.projectId) + + deleteAttachment: (toDeleteAttachment) -> + title = @translate.instant("ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT") + message = @translate.instant("ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT", { + fileName: toDeleteAttachment.getIn(['file', 'name']) + }) + + return @confirm.askOnDelete(title, message) + .then (askResponse) => + onError = () => + message = @translate.instant("ATTACHMENT.ERROR_DELETE_ATTACHMENT", {errorMessage: message}) + @confirm.notify("error", null, message) + askResponse.finish(false) + + onSuccess = () => askResponse.finish() + + @attachmentsFullService.deleteAttachment(toDeleteAttachment, @.type).then(onSuccess, onError) + + reorderAttachment: (attachment, newIndex) -> + @attachmentsFullService.reorderAttachment(@.type, attachment, newIndex) + + updateAttachment: (toUpdateAttachment) -> + @attachmentsFullService.updateAttachment(toUpdateAttachment, @.type) + +angular.module("taigaComponents").controller("AttachmentsFull", AttachmentsFullController) diff --git a/app/modules/components/attachments-full/attachments-full.controller.spec.coffee b/app/modules/components/attachments-full/attachments-full.controller.spec.coffee new file mode 100644 index 00000000..e45321af --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.controller.spec.coffee @@ -0,0 +1,215 @@ +### +# Copyright (C) 2014-2015 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: attchments.controller.spec.coffee +### + +describe "AttachmentsController", -> + $provide = null + $controller = null + mocks = {} + + _mockConfirm = -> + mocks.confirm = {} + + $provide.value("$tgConfirm", mocks.confirm) + + _mockTranslate = -> + mocks.translate = { + instant: sinon.stub() + } + + $provide.value("$translate", mocks.translate) + + _mockConfig = -> + mocks.config = { + get: sinon.stub() + } + + $provide.value("$tgConfig", mocks.config) + + _mockStorage = -> + mocks.storage = { + get: sinon.stub() + } + + $provide.value("$tgStorage", mocks.storage) + + _mockAttachmetsFullService = -> + mocks.attachmentsFullService = {} + + $provide.value("tgAttachmentsFullService", mocks.attachmentsFullService) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockConfirm() + _mockTranslate() + _mockConfig() + _mockStorage() + _mockAttachmetsFullService() + + return null + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaComponents" + + _setup() + + it "toggle deprecated visibility", () -> + + mocks.attachmentsFullService.toggleDeprecatedsVisible = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + ctrl.toggleDeprecatedsVisible() + + expect(mocks.attachmentsFullService.toggleDeprecatedsVisible).to.be.calledOnce + + it "add attachment", () -> + mocks.attachmentsFullService.addAttachment = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + file = Immutable.Map() + + ctrl.projectId = 3 + ctrl.objId = 30 + ctrl.type = 'us' + ctrl.mode = 'list' + + ctrl.addAttachment(file) + + expect(mocks.attachmentsFullService.addAttachment).to.have.been.calledWith(3, 30, 'us', file, true) + + it "add attachments", () -> + ctrl = $controller("AttachmentsFull") + + ctrl.attachments = Immutable.List() + ctrl.addAttachment = sinon.spy() + + files = [ + {}, + {}, + {} + ] + + ctrl.addAttachments(files) + + expect(ctrl.addAttachment).to.have.callCount(3) + + describe "deleteattachments", () -> + it "success attachment", (done) -> + deleteFile = Immutable.Map() + + mocks.attachmentsFullService.deleteAttachment = sinon.stub() + mocks.attachmentsFullService.deleteAttachment.withArgs(deleteFile, 'us').promise().resolve() + + askResponse = { + finish: sinon.spy() + } + + mocks.translate.instant.withArgs('ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT').returns('title') + mocks.translate.instant.withArgs('ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT').returns('message') + + mocks.confirm.askOnDelete = sinon.stub() + mocks.confirm.askOnDelete.withArgs('title', 'message').promise().resolve(askResponse) + + ctrl = $controller("AttachmentsFull") + + ctrl.type = 'us' + + ctrl.deleteAttachment(deleteFile).then () -> + expect(askResponse.finish).have.been.calledOnce + done() + + it "error attachment", (done) -> + deleteFile = Immutable.Map() + + mocks.attachmentsFullService.deleteAttachment = sinon.stub() + mocks.attachmentsFullService.deleteAttachment.withArgs(deleteFile, 'us').promise().reject() + + askResponse = { + finish: sinon.spy() + } + + mocks.translate.instant.withArgs('ATTACHMENT.TITLE_LIGHTBOX_DELETE_ATTACHMENT').returns('title') + mocks.translate.instant.withArgs('ATTACHMENT.MSG_LIGHTBOX_DELETE_ATTACHMENT').returns('message') + mocks.translate.instant.withArgs('ATTACHMENT.ERROR_DELETE_ATTACHMENT').returns('error') + + mocks.confirm.askOnDelete = sinon.stub() + mocks.confirm.askOnDelete.withArgs('title', 'message').promise().resolve(askResponse) + + mocks.confirm.notify = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + ctrl.type = 'us' + + ctrl.deleteAttachment(deleteFile).then () -> + expect(askResponse.finish.withArgs(false)).have.been.calledOnce + expect(mocks.confirm.notify.withArgs('error', null, 'error')) + done() + + it "loadAttachments", () -> + mocks.attachmentsFullService.loadAttachments = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + ctrl.projectId = 3 + ctrl.objId = 30 + ctrl.type = 'us' + + ctrl.loadAttachments() + + expect(mocks.attachmentsFullService.loadAttachments).to.have.been.calledWith('us', 30, 3) + + it "reorder attachments", () -> + mocks.attachmentsFullService.reorderAttachment = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + file = Immutable.Map() + + ctrl.projectId = 3 + ctrl.objId = 30 + ctrl.type = 'us' + + ctrl.reorderAttachment(file, 5) + + expect(mocks.attachmentsFullService.reorderAttachment).to.have.been.calledWith('us', file, 5) + + it "update attachment", () -> + mocks.attachmentsFullService.updateAttachment = sinon.spy() + + ctrl = $controller("AttachmentsFull") + + file = Immutable.Map() + + ctrl.type = 'us' + + ctrl.updateAttachment(file, 5) + + expect(mocks.attachmentsFullService.updateAttachment).to.have.been.calledWith(file, 'us') diff --git a/app/modules/components/attachments-full/attachments-full.directive.coffee b/app/modules/components/attachments-full/attachments-full.directive.coffee new file mode 100644 index 00000000..5200feb5 --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.directive.coffee @@ -0,0 +1,42 @@ +### +# Copyright (C) 2014-2015 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: attchments-full.directive.coffee +### + +bindOnce = @.taiga.bindOnce + +AttachmentsFullDirective = () -> + link = (scope, el, attrs, ctrl) -> + bindOnce scope, 'vm.objId', (value) -> + ctrl.loadAttachments() + + return { + scope: {}, + bindToController: { + type: "@", + objId: "=" + projectId: "=" + }, + controller: "AttachmentsFull", + controllerAs: "vm", + templateUrl: "components/attachments-full/attachments-full.html", + link: link + } + +AttachmentsFullDirective.$inject = [] + +angular.module("taigaComponents").directive("tgAttachmentsFull", AttachmentsFullDirective) diff --git a/app/modules/components/attachments-full/attachments-full.jade b/app/modules/components/attachments-full/attachments-full.jade new file mode 100644 index 00000000..0e3852b4 --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.jade @@ -0,0 +1,100 @@ +section.attachments(tg-attachments-drop="vm.addAttachments(files, false)") + .attachments-header + h3.attachments-title #[span.attachments-num {{vm.attachments.size}}] #[span.attachments-text(translate="ATTACHMENT.SECTION_NAME")] + .options + button.view-gallery( + ng-class="{'is-active': vm.mode == 'gallery'}" + ng-if="vm.attachments.size" + ng-click="vm.setMode('gallery')" + title="{{ 'ATTACHMENT.GALLERY_VIEW_MODE' | translate }}" + ) + include ../../../svg/gallery.svg + button.view-list( + ng-class="{'is-active': vm.mode == 'list'}" + ng-if="vm.attachments.size" + ng-click="vm.setMode('list')" + title="{{ 'ATTACHMENT.LIST_VIEW_MODE' | translate }}" + ) + include ../../../svg/list.svg + .add-attach( + tg-check-permission="modify_{{vm.type}}" + title!="{{'ATTACHMENT.ADD' | translate}}" + ) + span.size-info( + ng-if="vm.maxFileSize", + translate="ATTACHMENT.MAX_FILE_SIZE", + translate-values="{ 'maxFileSize': vm.maxFileSize}" + ) + + label.add-attachment-button(for="add-attach") + include ../../../svg/add.svg + + input( + id="add-attach", + type="file", + ng-model="files", + multiple="multiple", + tg-file-change="vm.addAttachments(files, true)" + ) + + .attachments-empty(ng-if="!vm.attachments.size && !vm.uploadingAttachments().length") + div {{'ATTACHMENT.DROP' | translate}} + .attachment-list.sortable(ng-if="vm.mode == 'list'") + div(tg-attachments-sortable="vm.reorderAttachment(attachment, index)") + div( + tg-repeat="attachment in vm.attachmentsVisible track by attachment.getIn(['file', 'id'])", + tg-bind-scope + ) + tg-attachment( + attachment="attachment", + on-delete="vm.deleteAttachment(attachment)", + on-update="vm.updateAttachment(attachment)", + type="vm.type" + ) + + .single-attachment(ng-repeat="file in vm.uploadingAttachments()") + .attachment-name + span.icon + include ../../../svg/attachment.svg + span {{file.name}} + .attachment-size + span {{file.size | sizeFormat}} + + .attachment-comments + span {{file.progressMessage}} + .percentage(ng-style="{'width': file.progressPercent}") + + a.more-attachments( + href="", + title="{{'ATTACHMENT.SHOW_DEPRECATED' | translate}}", + ng-if="vm.deprecatedsCount > 0", + ng-click="vm.toggleDeprecatedsVisible()" + ) + span.text( + ng-show="!vm.deprecatedsVisible", + translate="ATTACHMENT.SHOW_DEPRECATED" + ) + span.text( + ng-show="vm.deprecatedsVisible", + translate="ATTACHMENT.HIDE_DEPRECATED" + ) + span.more-attachments-num( + translate="ATTACHMENT.COUNT_DEPRECATED", + translate-values="{counter: '{{vm.deprecatedsCount}}'}" + ) + + .attachment-gallery(ng-if="vm.mode == 'gallery'") + tg-attachment-gallery.attachment-gallery-container( + tg-repeat="attachment in vm.attachmentsVisible track by attachment.getIn(['file', 'id'])" + attachment="attachment", + on-delete="vm.deleteAttachment(attachment)", + on-update="vm.updateAttachment(attachment)", + type="vm.type" + ) + .single-attachment(ng-repeat="file in vm.uploadingAttachments()") + .loading-container + img.loading-spinner( + src="/#{v}/svg/spinner-circle.svg", + alt="{{'COMMON.LOADING' | translate}}" + ) + .attachment-data {{file.progressMessage}} diff --git a/app/modules/components/attachments-full/attachments-full.service.coffee b/app/modules/components/attachments-full/attachments-full.service.coffee new file mode 100644 index 00000000..8f9a83a3 --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.service.coffee @@ -0,0 +1,136 @@ +### +# Copyright (C) 2014-2016 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: attachments-full.service.coffee +### + +class AttachmentsFullService extends taiga.Service + @.$inject = [ + "tgAttachmentsService", + "$rootScope" + ] + + constructor: (@attachmentsService, @rootScope) -> + @._attachments = Immutable.List() + @._deprecatedsCount = 0 + @._attachmentsVisible = Immutable.List() + @._deprecatedsVisible = false + @.uploadingAttachments = [] + + taiga.defineImmutableProperty @, 'attachments', () => return @._attachments + taiga.defineImmutableProperty @, 'deprecatedsCount', () => return @._deprecatedsCount + taiga.defineImmutableProperty @, 'attachmentsVisible', () => return @._attachmentsVisible + taiga.defineImmutableProperty @, 'deprecatedsVisible', () => return @._deprecatedsVisible + + toggleDeprecatedsVisible: () -> + @._deprecatedsVisible = !@._deprecatedsVisible + @.regenerate() + + regenerate: () -> + @._deprecatedsCount = @._attachments.count (it) -> it.getIn(['file', 'is_deprecated']) + + if @._deprecatedsVisible + @._attachmentsVisible = @._attachments + else + @._attachmentsVisible = @._attachments.filter (it) -> !it.getIn(['file', 'is_deprecated']) + + addAttachment: (projectId, objId, type, file, editable = true) -> + return new Promise (resolve, reject) => + if @attachmentsService.validate(file) + @.uploadingAttachments.push(file) + + promise = @attachmentsService.upload(file, objId, projectId, type) + promise.then (file) => + @.uploadingAttachments = @.uploadingAttachments.filter (uploading) -> + return uploading.name != file.get('name') + + attachment = Immutable.Map() + + attachment = attachment.merge({ + file: file, + editable: editable, + loading: false + }) + + @._attachments = @._attachments.push(attachment) + + @.regenerate() + + @rootScope.$broadcast("attachment:create") + + resolve(attachment) + else + reject(file) + + loadAttachments: (type, objId, projectId)-> + @attachmentsService.list(type, objId, projectId).then (files) => + @._attachments = files.map (file) -> + attachment = Immutable.Map() + + return attachment.merge({ + loading: false, + editable: false, + file: file + }) + + @.regenerate() + + deleteAttachment: (toDeleteAttachment, type) -> + onSuccess = () => + @._attachments = @._attachments.filter (attachment) -> attachment != toDeleteAttachment + + @.regenerate() + + return @attachmentsService.delete(type, toDeleteAttachment.getIn(['file', 'id'])).then(onSuccess) + + reorderAttachment: (type, attachment, newIndex) -> + oldIndex = @.attachments.findIndex (it) -> it == attachment + return if oldIndex == newIndex + + attachments = @.attachments.remove(oldIndex) + attachments = attachments.splice(newIndex, 0, attachment) + attachments = attachments.map (x, i) -> x.setIn(['file', 'order'], i + 1) + + promises = [] + attachments.forEach (attachment) => + patch = {order: attachment.getIn(['file', 'order'])} + + promises.push @attachmentsService.patch(attachment.getIn(['file', 'id']), type, patch) + + return Promise.all(promises).then () => + @._attachments = attachments + + @.regenerate() + + updateAttachment: (toUpdateAttachment, type) -> + index = @._attachments.findIndex (attachment) -> + return attachment.getIn(['file', 'id']) == toUpdateAttachment.getIn(['file', 'id']) + + oldAttachment = @._attachments.get(index) + + patch = taiga.patch(oldAttachment.get('file'), toUpdateAttachment.get('file')) + + if toUpdateAttachment.get('loading') + @._attachments = @._attachments.set(index, toUpdateAttachment) + + @.regenerate() + else + return @attachmentsService.patch(toUpdateAttachment.getIn(['file', 'id']), type, patch).then () => + @._attachments = @._attachments.set(index, toUpdateAttachment) + + @.regenerate() + +angular.module("taigaComponents").service("tgAttachmentsFullService", AttachmentsFullService) diff --git a/app/modules/components/attachments-full/attachments-full.service.spec.coffee b/app/modules/components/attachments-full/attachments-full.service.spec.coffee new file mode 100644 index 00000000..b5f1179c --- /dev/null +++ b/app/modules/components/attachments-full/attachments-full.service.spec.coffee @@ -0,0 +1,236 @@ +### +# Copyright (C) 2014-2015 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: attchments-full.service.spec.coffee +### + +describe "tgAttachmentsFullService", -> + $provide = null + attachmentsFullService = null + mocks = {} + + _mockAttachmentsService = -> + mocks.attachmentsService = { + upload: sinon.stub() + } + + $provide.value("tgAttachmentsService", mocks.attachmentsService) + + _mockRootScope = -> + mocks.rootScope = {} + + $provide.value("$rootScope", mocks.rootScope) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockAttachmentsService() + _mockRootScope() + + return null + + _inject = -> + inject (_tgAttachmentsFullService_) -> + attachmentsFullService = _tgAttachmentsFullService_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaComponents" + + _setup() + + it "generate, refresh deprecated counter", () -> + attachments = Immutable.fromJS([ + { + file: { + is_deprecated: false + } + }, + { + file: { + is_deprecated: true + } + }, + { + file: { + is_deprecated: true + } + }, + { + file: { + is_deprecated: false + } + }, + { + file: { + is_deprecated: true + } + } + ]) + + attachmentsFullService._attachments = attachments + + attachmentsFullService.regenerate() + + expect(attachmentsFullService._deprecatedsCount).to.be.equal(3) + + it "toggle deprecated visibility", () -> + attachmentsFullService._deprecatedsVisible = false + + attachmentsFullService.regenerate = sinon.spy() + + attachmentsFullService.toggleDeprecatedsVisible() + + expect(attachmentsFullService.deprecatedsVisible).to.be.true + expect(attachmentsFullService.regenerate).to.be.calledOnce + + describe "add attachments", () -> + it "valid attachment", (done) -> + projectId = 1 + objId = 2 + type = "issue" + + file = Immutable.fromJS({ + file: {}, + name: 'test', + size: 3000 + }) + + mocks.attachmentsService.validate = sinon.stub() + mocks.attachmentsService.validate.withArgs(file).returns(true) + + mocks.attachmentsService.upload = sinon.stub() + mocks.attachmentsService.upload.promise().resolve(file) + + mocks.rootScope.$broadcast = sinon.spy() + + attachmentsFullService._attachments = Immutable.List() + + attachmentsFullService.addAttachment(projectId, objId, type, file).then () -> + expect(mocks.rootScope.$broadcast).have.been.calledWith('attachment:create') + expect(attachmentsFullService.attachments.count()).to.be.equal(1) + done() + + it "invalid attachment", () -> + file = Immutable.fromJS({ + file: {}, + name: 'test', + size: 3000 + }) + + mocks.attachmentsService.validate = sinon.stub() + mocks.attachmentsService.validate.withArgs(file).returns(false) + + mocks.attachmentsService.upload = sinon.stub() + mocks.attachmentsService.upload.promise().resolve(file) + + mocks.rootScope.$broadcast = sinon.spy() + + attachmentsFullService._attachments = Immutable.List() + + attachmentsFullService.addAttachment(file).then null, () -> + expect(attachmentsFullService._attachments.count()).to.be.equal(0) + + describe "deleteattachments", () -> + it "success attachment", (done) -> + mocks.attachmentsService.delete = sinon.stub() + mocks.attachmentsService.delete.withArgs('us', 2).promise().resolve() + + attachmentsFullService.regenerate = sinon.spy() + attachmentsFullService._attachments = Immutable.fromJS([ + { + file: {id: 1} + }, + { + file: {id: 2} + }, + { + file: {id: 3} + }, + { + file: {id: 4} + } + ]) + + deleteFile = attachmentsFullService._attachments.get(1) + + attachmentsFullService.deleteAttachment(deleteFile, 'us').then () -> + expect(attachmentsFullService.regenerate).have.been.calledOnce + expect(attachmentsFullService.attachments.size).to.be.equal(3) + done() + + it "reorder attachments", (done) -> + attachments = Immutable.fromJS([ + {file: {id: 0, is_deprecated: false, order: 0}}, + {file: {id: 1, is_deprecated: true, order: 1}}, + {file: {id: 2, is_deprecated: true, order: 2}}, + {file: {id: 3, is_deprecated: false, order: 3}}, + {file: {id: 4, is_deprecated: true, order: 4}} + ]) + + mocks.attachmentsService.patch = sinon.stub() + mocks.attachmentsService.patch.promise().resolve() + + attachmentsFullService._attachments = attachments + + attachmentsFullService.reorderAttachment('us', attachments.get(1), 0).then () -> + expect(attachmentsFullService.attachments.get(0)).to.be.equal(attachments.get(1)) + done() + + it "update attachment", () -> + attachments = Immutable.fromJS([ + {file: {id: 0, is_deprecated: false, order: 0}}, + {file: {id: 1, is_deprecated: true, order: 1}}, + {file: {id: 2, is_deprecated: true, order: 2}}, + {file: {id: 3, is_deprecated: false, order: 3}}, + {file: {id: 4, is_deprecated: true, order: 4}} + ]) + + attachment = attachments.get(1) + attachment = attachment.setIn(['file', 'is_deprecated'], false) + + mocks.attachmentsService.patch = sinon.stub() + mocks.attachmentsService.patch.withArgs(1, 'us', {is_deprecated: false}).promise().resolve() + + attachmentsFullService._attachments = attachments + + attachmentsFullService.updateAttachment(attachment, 'us').then () -> + expect(attachmentsFullService.attachments.get(1).toJS()).to.be.eql(attachment.toJS()) + + it "update loading attachment", () -> + attachments = Immutable.fromJS([ + {file: {id: 0, is_deprecated: false, order: 0}}, + {loading: true, file: {id: 1, is_deprecated: true, order: 1}}, + {file: {id: 2, is_deprecated: true, order: 2}}, + {file: {id: 3, is_deprecated: false, order: 3}}, + {file: {id: 4, is_deprecated: true, order: 4}} + ]) + + attachment = attachments.get(1) + attachment = attachment.setIn(['file', 'is_deprecated'], false) + + mocks.attachmentsService.patch = sinon.stub() + mocks.attachmentsService.patch.withArgs(1, 'us', {is_deprecated: false}).promise().resolve() + + attachmentsFullService._attachments = attachments + + attachmentsFullService.updateAttachment(attachment, 'us') + + expect(attachmentsFullService.attachments.get(1).toJS()).to.be.eql(attachment.toJS()) diff --git a/app/modules/components/attachments-simple/attachment-simple.scss b/app/modules/components/attachments-simple/attachment-simple.scss new file mode 100644 index 00000000..e69de29b diff --git a/app/modules/components/attachments-simple/attachments-simple.controller.coffee b/app/modules/components/attachments-simple/attachments-simple.controller.coffee new file mode 100644 index 00000000..cc5c2e48 --- /dev/null +++ b/app/modules/components/attachments-simple/attachments-simple.controller.coffee @@ -0,0 +1,47 @@ +### +# Copyright (C) 2014-2015 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: attchments-simple.controller.coffee +### + +class AttachmentsSimpleController + @.$inject = [ + "tgAttachmentsService" + ] + + constructor: (@attachmentsService) -> + + addAttachment: (file) -> + attachment = Immutable.fromJS({ + file: file, + name: file.name, + size: file.size + }) + + if @attachmentsService.validate(file) + @.attachments = @.attachments.push(attachment) + + @.onAdd({attachment: attachment}) if @.onAdd + + addAttachments: (files) -> + _.forEach files, @.addAttachment.bind(this) + + deleteAttachment: (toDeleteAttachment) -> + @.attachments = @.attachments.filter (attachment) -> attachment != toDeleteAttachment + + @.onDelete({attachment: toDeleteAttachment}) if @.onDelete + +angular.module("taigaComponents").controller("AttachmentsSimple", AttachmentsSimpleController) diff --git a/app/modules/components/attachments-simple/attachments-simple.controller.spec.coffee b/app/modules/components/attachments-simple/attachments-simple.controller.spec.coffee new file mode 100644 index 00000000..b2f6b0f3 --- /dev/null +++ b/app/modules/components/attachments-simple/attachments-simple.controller.spec.coffee @@ -0,0 +1,96 @@ +### +# Copyright (C) 2014-2015 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: attchment.controller.spec.coffee +### + +describe "AttachmentsSimple", -> + $provide = null + $controller = null + mocks = {} + scope = null + + _mockAttachmentsService = -> + mocks.attachmentsService = {} + + $provide.value("tgAttachmentsService", mocks.attachmentsService) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockAttachmentsService() + + return null + + _inject = -> + inject (_$controller_, $rootScope) -> + $controller = _$controller_ + scope = $rootScope.$new() + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaComponents" + + _setup() + + it "add attachment", () -> + file = { + name: 'name', + size: 1000 + } + + mocks.attachmentsService.validate = sinon.stub() + mocks.attachmentsService.validate.withArgs(file).returns(true) + + ctrl = $controller("AttachmentsSimple", { + $scope: scope + }, { + attachments: Immutable.List() + }) + + ctrl.onAdd = sinon.spy() + + ctrl.addAttachment(file) + + expect(ctrl.attachments.size).to.be.equal(1) + expect(ctrl.onAdd).to.have.been.calledOnce + + it "delete attachment", () -> + attachments = Immutable.fromJS([ + {id: 1}, + {id: 2}, + {id: 3} + ]) + + ctrl = $controller("AttachmentsSimple", { + $scope: scope + }, { + attachments: attachments + }) + + ctrl.onDelete = sinon.spy() + + + attachment = attachments.get(1) + + ctrl.deleteAttachment(attachment) + + expect(ctrl.attachments.size).to.be.equal(2) + expect(ctrl.onDelete.withArgs({attachment: attachment})).to.have.been.calledOnce diff --git a/app/modules/components/attachments-simple/attachments-simple.directive.coffee b/app/modules/components/attachments-simple/attachments-simple.directive.coffee new file mode 100644 index 00000000..08bb5063 --- /dev/null +++ b/app/modules/components/attachments-simple/attachments-simple.directive.coffee @@ -0,0 +1,38 @@ +### +# Copyright (C) 2014-2015 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: attchments-simple.directive.coffee +### + +AttachmentsSimpleDirective = () -> + link = (scope, el, attrs, ctrl) -> + + return { + scope: {}, + bindToController: { + attachments: "=", + onAdd: "&", + onDelete: "&" + }, + controller: "AttachmentsSimple", + controllerAs: "vm", + templateUrl: "components/attachments-simple/attachments-simple.html", + link: link + } + +AttachmentsSimpleDirective.$inject = [] + +angular.module("taigaComponents").directive("tgAttachmentsSimple", AttachmentsSimpleDirective) diff --git a/app/modules/components/attachments-simple/attachments-simple.jade b/app/modules/components/attachments-simple/attachments-simple.jade new file mode 100644 index 00000000..d40aca41 --- /dev/null +++ b/app/modules/components/attachments-simple/attachments-simple.jade @@ -0,0 +1,38 @@ +//- section.attachments(tg-attachments-drop="vm.addAttachments(files)") + +section.attachments(tg-attachments-drop="vm.addAttachments(files)") + .attachments-header + h3.attachments-title #[span.attachments-num {{vm.attachments.size}}] #[span.attachments-text(translate="ATTACHMENT.SECTION_NAME")] + .add-attach(title!="{{'ATTACHMENT.ADD' | translate}}") + span.size-info( + translate="ATTACHMENT.MAX_FILE_SIZE" + translate-values="{ 'maxFileSize': vm.maxFileSizeFormated}" + ng-if="vm.maxFileSize" + ) + label.add-attachment-button(for="add-attach") + include ../../../svg/add.svg + input( + id="add-attach" + type="file" + multiple="multiple" + ng-model="files" + tg-file-change="vm.addAttachments(files)" + ) + .attachments-empty(ng-if="!vm.attachments.size") + div {{'ATTACHMENT.DROP' | translate}} + .attachment-body.attachment-list + .single-attachment(tg-repeat="attachment in vm.attachments track by $index") + .attachment-name + span.icon + include ../../../svg/attachment.svg + span {{attachment.get('name')}} + .attachment-size + span {{attachment.get('size') | sizeFormat}} + + .attachment-settings + a.settings.attachment-delete( + href="#" + title="{{'COMMON.DELETE' | translate}}" + ng-click="vm.deleteAttachment(attachment)" + ) + include ../../../svg/remove.svg diff --git a/app/modules/components/attachments-sortable/attachments-sortable.directive.coffee b/app/modules/components/attachments-sortable/attachments-sortable.directive.coffee new file mode 100644 index 00000000..c220e9b2 --- /dev/null +++ b/app/modules/components/attachments-sortable/attachments-sortable.directive.coffee @@ -0,0 +1,52 @@ +### +# Copyright (C) 2014-2016 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: attachments-sortable.directive.coffee +### + +AttachmentSortableDirective = ($parse) -> + link = (scope, el, attrs) -> + callback = $parse(attrs.tgAttachmentsSortable) + + el.sortable({ + items: "div[tg-bind-scope]" + handle: "a.settings.icon.icon-drag-v" + containment: ".attachments" + dropOnEmpty: true + helper: 'clone' + scroll: false + tolerance: "pointer" + placeholder: "sortable-placeholder single-attachment" + }) + + el.on "sortstop", (event, ui) -> + attachment = ui.item.scope().attachment + newIndex = ui.item.index() + + scope.$apply () -> + callback(scope, {attachment: attachment, index: newIndex}) + + scope.$on "$destroy", -> el.off() + + return { + link: link + } + +AttachmentSortableDirective.$inject = [ + "$parse" +] + +angular.module("taigaComponents").directive("tgAttachmentsSortable", AttachmentSortableDirective) diff --git a/app/modules/components/auto-select/auto-select.directive.coffee b/app/modules/components/auto-select/auto-select.directive.coffee new file mode 100644 index 00000000..5ac889cc --- /dev/null +++ b/app/modules/components/auto-select/auto-select.directive.coffee @@ -0,0 +1,30 @@ +### +# Copyright (C) 2014-2016 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: auto-select.directive.coffee +### + +AutoSelectDirective = ($timeout) -> + return { + link: (scope, elm) -> + $timeout () -> elm[0].select() + } + +AutoSelectDirective.$inject = [ + '$timeout' +] + +angular.module("taigaComponents").directive("tgAutoSelect", AutoSelectDirective) diff --git a/app/modules/components/file-change/file-change.directive.coffee b/app/modules/components/file-change/file-change.directive.coffee new file mode 100644 index 00000000..3c94f94a --- /dev/null +++ b/app/modules/components/file-change/file-change.directive.coffee @@ -0,0 +1,39 @@ +### +# Copyright (C) 2014-2016 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: file-change.directive.coffee +### + +FileChangeDirective = ($parse) -> + link = (scope, el, attrs, ctrl) -> + eventAttr = $parse(attrs.tgFileChange) + + el.on 'change', (event) -> + scope.$apply () -> eventAttr(scope, {files: event.currentTarget.files}) + + scope.$on "$destroy", -> el.off() + + return { + require: "ngModel", + restrict: "A", + link: link + } + +FileChangeDirective.$inject = [ + "$parse" +] + +angular.module("taigaComponents").directive("tgFileChange", FileChangeDirective) diff --git a/app/modules/components/live-announcement/live-announcement.directive.coffee b/app/modules/components/live-announcement/live-announcement.directive.coffee index 97f037d8..b175ebf3 100644 --- a/app/modules/components/live-announcement/live-announcement.directive.coffee +++ b/app/modules/components/live-announcement/live-announcement.directive.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # Copyright (C) 2014-2015 Jesús Espino Garcia # Copyright (C) 2014-2015 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/modules/components/project-logo-src/project-logo-src.directive.coffee b/app/modules/components/project-logo-src/project-logo-src.directive.coffee new file mode 100644 index 00000000..2b7b8804 --- /dev/null +++ b/app/modules/components/project-logo-src/project-logo-src.directive.coffee @@ -0,0 +1,77 @@ +### +# Copyright (C) 2014-2016 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: project-logo.directive.coffee +### + + +IMAGES = [ + "/#{window._version}/images/project-logos/project-logo-01.png" + "/#{window._version}/images/project-logos/project-logo-02.png" + "/#{window._version}/images/project-logos/project-logo-03.png" + "/#{window._version}/images/project-logos/project-logo-04.png" + "/#{window._version}/images/project-logos/project-logo-05.png" +] + +COLORS = [ + "rgba( 153, 214, 220, 1 )" + "rgba( 213, 156, 156, 1 )" + "rgba( 214, 161, 212, 1 )" + "rgba( 164, 162, 219, 1 )" + "rgba( 152, 224, 168, 1 )" +] + +LOGOS = _.cartesianProduct(IMAGES, COLORS) + + +ProjectLogoSrcDirective = ($parse) -> + _getDefaultProjectLogo = (project) -> + key = "#{project.get("slug")}-#{project.get("id")}" + idx = murmurhash3_32_gc(key, 42) %% LOGOS.length + logo = LOGOS[idx] + + return { src: logo[0], color: logo[1] } + + link = (scope, el, attrs) -> + scope.$watch "project", (project) -> + project = Immutable.fromJS(project) # Necesary for old code + + return if not project + + projectLogo = project.get('logo_small_url') + + if projectLogo + el.attr("src", projectLogo) + el.css('background', "") + else + logo = _getDefaultProjectLogo(project) + el.attr("src", logo.src) + el.css('background', logo.color) + + scope.$on "$destroy", -> el.off() + + return { + link: link + scope: { + project: "=tgProjectLogoSrc" + } + } + +ProjectLogoSrcDirective.$inject = [ + "$parse" +] + +angular.module("taigaComponents").directive("tgProjectLogoSrc", ProjectLogoSrcDirective) diff --git a/app/modules/components/project-menu/project-menu.controller.coffee b/app/modules/components/project-menu/project-menu.controller.coffee index fdd381a2..cf5710a3 100644 --- a/app/modules/components/project-menu/project-menu.controller.coffee +++ b/app/modules/components/project-menu/project-menu.controller.coffee @@ -72,13 +72,14 @@ class ProjectMenuController _getActiveSection: () -> sectionName = @projectService.section + sectionsBreadcrumb = @projectService.sectionsBreadcrumb indexBacklog = sectionsBreadcrumb.lastIndexOf("backlog") indexKanban = sectionsBreadcrumb.lastIndexOf("kanban") if indexBacklog != -1 || indexKanban != -1 - if indexKanban == -1 || indexBacklog < indexKanban + if indexKanban == -1 || indexBacklog > indexKanban oldSectionName = "backlog" else oldSectionName = "kanban" diff --git a/app/modules/components/project-menu/project-menu.directive.coffee b/app/modules/components/project-menu/project-menu.directive.coffee index 67ff2fd4..ee3da69b 100644 --- a/app/modules/components/project-menu/project-menu.directive.coffee +++ b/app/modules/components/project-menu/project-menu.directive.coffee @@ -31,6 +31,16 @@ ProjectMenuDirective = (projectService, lightboxFactory) -> return projectService.project ), projectChange + scope.vm.fixed = false + $(window).on "scroll", () -> + position = $(window).scrollTop() + if position > 100 && scope.vm.fixed == false + scope.vm.fixed = true + scope.$digest() + else if position < 100 && scope.vm.fixed == true + scope.vm.fixed = false + scope.$digest() + return { scope: {}, controller: "ProjectMenu", diff --git a/app/modules/components/project-menu/project-menu.jade b/app/modules/components/project-menu/project-menu.jade index 9ff22d8e..e411f98a 100644 --- a/app/modules/components/project-menu/project-menu.jade +++ b/app/modules/components/project-menu/project-menu.jade @@ -1,4 +1,7 @@ -nav.menu(ng-if="vm.project") +nav.menu( + ng-if="vm.project" + ng-class="{'menu-fixed': vm.fixed}", +) div(class="menu-container") ul(class="main-nav") li(id="nav-search") diff --git a/app/modules/components/terms-of-service-and-privacy-policy-notice/terms-of-service-and-privacy-policy-notice.directive.coffee b/app/modules/components/terms-of-service-and-privacy-policy-notice/terms-of-service-and-privacy-policy-notice.directive.coffee index e10e47ea..ae8b4b56 100644 --- a/app/modules/components/terms-of-service-and-privacy-policy-notice/terms-of-service-and-privacy-policy-notice.directive.coffee +++ b/app/modules/components/terms-of-service-and-privacy-policy-notice/terms-of-service-and-privacy-policy-notice.directive.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/modules/components/vote-button/vote-button.jade b/app/modules/components/vote-button/vote-button.jade index 457f966b..24d63b04 100644 --- a/app/modules/components/vote-button/vote-button.jade +++ b/app/modules/components/vote-button/vote-button.jade @@ -16,7 +16,6 @@ a.vote-inner( ) {{ vm.item.total_voters }} //- Anonymous user button - span.vote-inner(ng-if="::!vm.user") span.track-icon include ../../../svg/upvote.svg diff --git a/app/modules/components/watch-button/watch-button-ticket.jade b/app/modules/components/watch-button/watch-button-ticket.jade new file mode 100644 index 00000000..e54d36fd --- /dev/null +++ b/app/modules/components/watch-button/watch-button-ticket.jade @@ -0,0 +1,37 @@ +div.ticket-watch-title( + title="{{ 'COMMON.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.watchers.length||0}:'messageformat' }}", + tg-loading="vm.loading" +) {{ vm.item.watchers.length }} {{'COMMON.WATCHERS.WATCHERS' | translate}} + +div.ticket-watch-inner + a.ticket-watch-button( + href="" + title="{{ 'COMMON.WATCH_BUTTON.BUTTON_TITLE' | translate }}" + ng-if="::vm.user" + ng-click="vm.toggleWatch()" + ng-class="{'active': vm.item.is_watcher, 'is-hover': vm.item.is_watcher && vm.isMouseOver}" + ng-mouseover="vm.showTextWhenMouseIsOver()" + ng-mouseleave="vm.showTextWhenMouseIsLeave()" + ) + span.track-icon + include ../../../svg/watch.svg + span( + ng-if="!vm.item.is_watcher", + translate="COMMON.WATCH_BUTTON.WATCH" + ) + span( + ng-if="vm.item.is_watcher && !vm.isMouseOver", + translate="COMMON.WATCH_BUTTON.WATCHING" + ) + span( + ng-if="vm.item.is_watcher && vm.isMouseOver", + translate="COMMON.WATCH_BUTTON.UNWATCH" + ) + + a.add-watcher( + href="" + title="{{'COMMON.WATCHERS.TITLE_ADD' | translate}}" + ng-click="vm.openWatchers()" + tg-check-permission="{{vm.getPerms()}}" + ) + span + {{'COMMON.WATCHERS.ADD' | translate}} diff --git a/app/modules/components/watch-button/watch-button.controller.coffee b/app/modules/components/watch-button/watch-button.controller.coffee index 39b16f57..99514424 100644 --- a/app/modules/components/watch-button/watch-button.controller.coffee +++ b/app/modules/components/watch-button/watch-button.controller.coffee @@ -20,9 +20,10 @@ class WatchButtonController @.$inject = [ "tgCurrentUserService", + "$rootScope" ] - constructor: (@currentUserService) -> + constructor: (@currentUserService, @rootScope) -> @.user = @currentUserService.getUser() @.isMouseOver = false @.loading = false @@ -33,6 +34,22 @@ class WatchButtonController showTextWhenMouseIsLeave: -> @.isMouseOver = false + openWatchers: -> + @rootScope.$broadcast("watcher:add", @.item) + + getPerms: -> + return "" if !@.item + + name = @.item._name + + perms = { + userstories: 'modify_us', + issues: 'modify_issue', + tasks: 'modify_task' + } + + return perms[name] + toggleWatch: -> @.loading = true diff --git a/app/modules/components/watch-button/watch-button.controller.spec.coffee b/app/modules/components/watch-button/watch-button.controller.spec.coffee index db06465b..41e95efb 100644 --- a/app/modules/components/watch-button/watch-button.controller.spec.coffee +++ b/app/modules/components/watch-button/watch-button.controller.spec.coffee @@ -100,3 +100,24 @@ describe "WatchButton", -> expect(ctrl.loading).to.be.false; done() + + + it "get permissions", () -> + $scope = $rootScope.$new() + + ctrl = $controller("WatchButton", $scope, { + item: {_name: 'tasks'} + }) + + perm = ctrl.getPerms() + expect(perm).to.be.equal('modify_task') + + ctrl.item = {_name: 'issues'} + + perm = ctrl.getPerms() + expect(perm).to.be.equal('modify_issue') + + ctrl.item = {_name: 'userstories'} + + perm = ctrl.getPerms() + expect(perm).to.be.equal('modify_us') diff --git a/app/modules/components/watch-button/watch-button.directive.coffee b/app/modules/components/watch-button/watch-button.directive.coffee index 12a9ac7b..a5797950 100644 --- a/app/modules/components/watch-button/watch-button.directive.coffee +++ b/app/modules/components/watch-button/watch-button.directive.coffee @@ -27,7 +27,8 @@ WatchButtonDirective = -> onUnwatch: "=" } controllerAs: "vm", - templateUrl: "components/watch-button/watch-button.html", + templateUrl: (item, attributes) -> + return "components/watch-button/watch-button-"+attributes.environment+".html" } angular.module("taigaComponents").directive("tgWatchButton", WatchButtonDirective) diff --git a/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.coffee b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.coffee new file mode 100644 index 00000000..ebecd4ab --- /dev/null +++ b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.coffee @@ -0,0 +1,50 @@ +### +# Copyright (C) 2014-2015 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: discover-home-order-by.controller.coffee +### + +class DiscoverHomeOrderByController + @.$inject = [ + '$translate' + ] + + constructor: (@translate) -> + @.is_open = false + + @.texts = { + week: @translate.instant('DISCOVER.FILTERS.WEEK'), + month: @translate.instant('DISCOVER.FILTERS.MONTH'), + year: @translate.instant('DISCOVER.FILTERS.YEAR'), + all: @translate.instant('DISCOVER.FILTERS.ALL_TIME') + } + + currentText: () -> + return @.texts[@.currentOrderBy] + + open: () -> + @.is_open = true + + close: () -> + @.is_open = false + + orderBy: (type) -> + @.currentOrderBy = type + @.is_open = false + + @.onChange({orderBy: @.currentOrderBy}) + +angular.module("taigaDiscover").controller("DiscoverHomeOrderBy", DiscoverHomeOrderByController) diff --git a/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.spec.coffee b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.spec.coffee new file mode 100644 index 00000000..db71dbb9 --- /dev/null +++ b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.controller.spec.coffee @@ -0,0 +1,96 @@ +### +# Copyright (C) 2014-2015 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: discover-home-order-by.controller.spec.coffee +### + +describe "DiscoverHomeOrderBy", -> + $provide = null + $controller = null + mocks = {} + + _mockTranslate = -> + mocks.translate = { + instant: sinon.stub() + } + + $provide.value("$translate", mocks.translate) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockTranslate() + + return null + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaDiscover" + + _setup() + + it "get current search text", () -> + mocks.translate.instant.withArgs('DISCOVER.FILTERS.WEEK').returns('week') + mocks.translate.instant.withArgs('DISCOVER.FILTERS.MONTH').returns('month') + + ctrl = $controller("DiscoverHomeOrderBy") + + ctrl.currentOrderBy = 'week' + text = ctrl.currentText() + + expect(text).to.be.equal('week') + + ctrl.currentOrderBy = 'month' + text = ctrl.currentText() + + expect(text).to.be.equal('month') + + it "open", () -> + ctrl = $controller("DiscoverHomeOrderBy") + + ctrl.is_open = false + + ctrl.open() + + expect(ctrl.is_open).to.be.true + + it "close", () -> + ctrl = $controller("DiscoverHomeOrderBy") + + ctrl.is_open = true + + ctrl.close() + + expect(ctrl.is_open).to.be.false + + it "order by", () -> + ctrl = $controller("DiscoverHomeOrderBy") + ctrl.onChange = sinon.spy() + + ctrl.orderBy('week') + + + expect(ctrl.currentOrderBy).to.be.equal('week') + expect(ctrl.is_open).to.be.false + expect(ctrl.onChange).to.have.been.calledWith({orderBy: 'week'}) diff --git a/app/modules/discover/components/discover-home-order-by/discover-home-order-by.directive.coffee b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.directive.coffee new file mode 100644 index 00000000..2def5e12 --- /dev/null +++ b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.directive.coffee @@ -0,0 +1,37 @@ +### +# Copyright (C) 2014-2015 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: discover-home-order-by.directive.coffee +### + +DiscoverHomeOrderByDirective = () -> + link = (scope, el, attrs) -> + + return { + controller: "DiscoverHomeOrderBy", + controllerAs: "vm", + bindToController: true, + templateUrl: "discover/components/discover-home-order-by/discover-home-order-by.html", + scope: { + currentOrderBy: "=orderBy", + onChange: "&" + }, + link: link + } + +DiscoverHomeOrderByDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgDiscoverHomeOrderBy", DiscoverHomeOrderByDirective) diff --git a/app/modules/discover/components/discover-home-order-by/discover-home-order-by.jade b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.jade new file mode 100644 index 00000000..fbaaa04c --- /dev/null +++ b/app/modules/discover/components/discover-home-order-by/discover-home-order-by.jade @@ -0,0 +1,12 @@ +.filter-highlighted(ng-mouseleave="vm.close()") + a.current-filter( + href="#" + ng-click="vm.open()" + ) {{vm.currentText()}} + span.icon-arrow-bottom + + ul.filter-list(ng-if="vm.is_open") + li(ng-click="vm.orderBy('week')") {{ 'DISCOVER.FILTERS.WEEK' | translate }} + li(ng-click="vm.orderBy('month')") {{ 'DISCOVER.FILTERS.MONTH' | translate }} + li(ng-click="vm.orderBy('year')") {{ 'DISCOVER.FILTERS.YEAR' | translate }} + li(ng-click="vm.orderBy('all')") {{ 'DISCOVER.FILTERS.ALL_TIME' | translate }} diff --git a/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.coffee b/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.coffee new file mode 100644 index 00000000..a7ca46c1 --- /dev/null +++ b/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.coffee @@ -0,0 +1,36 @@ +### +# Copyright (C) 2014-2015 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: discover-search-bar.controller.coffee +### + +class DiscoverSearchBarController + @.$inject = [ + 'tgDiscoverProjectsService' + ] + + constructor: (@discoverProjectsService) -> + taiga.defineImmutableProperty @, 'projects', () => return @discoverProjectsService.projectsCount + + @discoverProjectsService.fetchStats() + + selectFilter: (filter) -> + @.onChange({filter: filter, q: @.q}) + + submitFilter: -> + @.onChange({filter: @.filter, q: @.q}) + +angular.module("taigaDiscover").controller("DiscoverSearchBar", DiscoverSearchBarController) diff --git a/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.spec.coffee b/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.spec.coffee new file mode 100644 index 00000000..36b5dd4e --- /dev/null +++ b/app/modules/discover/components/discover-search-bar/discover-search-bar.controller.spec.coffee @@ -0,0 +1,72 @@ +### +# Copyright (C) 2014-2015 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: doscover-search-bar.controller.spec.coffee +### + +describe "DiscoverSearchBarController", -> + $provide = null + $controller = null + mocks = {} + + _mockDiscoverProjectsService = -> + mocks.discoverProjectsService = { + fetchStats: sinon.spy() + } + + $provide.value('tgDiscoverProjectsService', mocks.discoverProjectsService) + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockDiscoverProjectsService() + + return null + + _setup = -> + _inject() + + beforeEach -> + module "taigaDiscover" + + _mocks() + _setup() + + it "select filter", () -> + ctrl = $controller("DiscoverSearchBar") + ctrl.onChange = sinon.spy() + ctrl.q = 'query' + + ctrl.selectFilter('text') + + expect(mocks.discoverProjectsService.fetchStats).to.have.been.called; + expect(ctrl.onChange).to.have.been.calledWith(sinon.match({filter: 'text', q: 'query'})); + + it "submit filter", () -> + ctrl = $controller("DiscoverSearchBar") + ctrl.filter = 'all' + ctrl.q = 'query' + ctrl.onChange = sinon.spy() + + ctrl.submitFilter() + + expect(mocks.discoverProjectsService.fetchStats).to.have.been.called; + expect(ctrl.onChange).to.have.been.calledWith(sinon.match({filter: 'all', q: 'query'})); diff --git a/app/modules/discover/components/discover-search-bar/discover-search-bar.directive.coffee b/app/modules/discover/components/discover-search-bar/discover-search-bar.directive.coffee new file mode 100644 index 00000000..9dab0fbc --- /dev/null +++ b/app/modules/discover/components/discover-search-bar/discover-search-bar.directive.coffee @@ -0,0 +1,38 @@ +### +# Copyright (C) 2014-2015 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: discover-search.directive.coffee +### + +DiscoverSearchBarDirective = () -> + link = (scope, el, attrs, ctrl) -> + + return { + controller: "DiscoverSearchBar", + controllerAs: "vm" + templateUrl: 'discover/components/discover-search-bar/discover-search-bar.html', + bindToController: true, + scope: { + q: "=" + filter: "=", + onChange: "&" + }, + link: link + } + +DiscoverSearchBarDirective.$inject = [] + +angular.module('taigaDiscover').directive('tgDiscoverSearchBar', DiscoverSearchBarDirective) diff --git a/app/modules/discover/components/discover-search-bar/discover-search-bar.jade b/app/modules/discover/components/discover-search-bar/discover-search-bar.jade new file mode 100644 index 00000000..201c6bca --- /dev/null +++ b/app/modules/discover/components/discover-search-bar/discover-search-bar.jade @@ -0,0 +1,71 @@ +div.discover-header + div.discover-header-inner + + h1.title {{ 'DISCOVER.DISCOVER_TITLE' | translate }} + + p.project-number( + ng-if="vm.projects", + translate="DISCOVER.DISCOVER_SUBTITLE", + translate-values="{ projects: '{{vm.projects}}'}" + translate-interpolation="messageformat" + ) + + form(ng-submit="vm.submitFilter()") + div.searchbox + input( + name="search" + type="text" + placeholder="{{ 'DISCOVER.SEARCH.INPUT_PLACEHOLDER' | translate }}" + ng-model="vm.q" + ) + a.search-button( + ng-click="vm.submitFilter()" + href="#" + title="{{ 'DISCOVER.SEARCH.ACTION_TITLE' | translate }}" + ) + include ../../../../svg/search.svg + + fieldset.searchbox-filters(ng-if="vm.filter") + input( + type='radio' + id="filter-all" + name="filter-search" + ) + label( + for="filter-all" + ng-click="vm.selectFilter('all')" + ng-class="{active: vm.filter == 'all'}", + ) {{ 'DISCOVER.FILTERS.ALL' | translate }} + + input( + type='radio' + id="filter-kanban" + name="filter-search" + ) + label( + for="filter-kanban" + ng-class="{active: vm.filter == 'kanban'}", + ng-click="vm.selectFilter('kanban')" + ) {{ 'DISCOVER.FILTERS.KANBAN' | translate }} + + input( + type='radio' + id="filter-scrum" + name="filter-search" + ) + label( + for="filter-scrum" + ng-class="{active: vm.filter == 'scrum'}", + ng-click="vm.selectFilter('scrum')" + ) {{ 'DISCOVER.FILTERS.SCRUM' | translate }} + + input( + type='radio' + id="filter-people" + name="filter-search" + ) + label( + for="filter-people" + ng-class="{active: vm.filter == 'people'}", + ng-click="vm.selectFilter('people')" + ) {{ 'DISCOVER.FILTERS.PEOPLE' | translate }} diff --git a/app/modules/discover/components/discover-search-bar/discover-search-bar.scss b/app/modules/discover/components/discover-search-bar/discover-search-bar.scss new file mode 100644 index 00000000..a45705b2 --- /dev/null +++ b/app/modules/discover/components/discover-search-bar/discover-search-bar.scss @@ -0,0 +1,54 @@ +.discover-header { + background: url('../images/discover.png') repeat-x bottom left $whitish; + margin-bottom: 2.5rem; + padding: 1rem 1rem 2rem; + text-align: center; + .discover-header-inner { + @include centered; + margin: 0 auto; + } + .title { + @extend %xxlarge; + margin-bottom: 0; + } + .project-number { + @extend %light; + @extend %large; + color: $primary; + } + form { + margin: 0 30%; + position: relative; + @include breakpoint(tablet) { + margin: 0 .5rem; + } + } + input[type="text"] { + background: $white; + border: 0; + padding: 1rem; + width: 100%; + &:focus { + outline-color: $primary-light; + } + &:-webkit-autofill { + background: rgba($primary-dark, .5); + } + } + .search-button { + position: absolute; + right: 1rem; + top: 1rem; + &:hover { + svg { + fill: $primary; + } + } + } + svg { + fill: $gray-light; + height: 1.5rem; + transition: all .2; + width: 1.5rem; + } +} diff --git a/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.coffee b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.coffee new file mode 100644 index 00000000..4c6e0c23 --- /dev/null +++ b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.coffee @@ -0,0 +1,46 @@ +### +# Copyright (C) 2014-2015 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: discover-search-list-header.controller.coffee +### + +class DiscoverSearchListHeaderController + @.$inject = [] + + constructor: () -> + @.like_is_open = @.orderBy.indexOf('-total_fans') == 0 + @.activity_is_open = @.orderBy.indexOf('-total_activity') == 0 + + openLike: () -> + @.like_is_open = true + @.activity_is_open = false + + @.setOrderBy('-total_fans_last_week') + + openActivity: () -> + @.activity_is_open = true + @.like_is_open = false + + @.setOrderBy('-total_activity_last_week') + + setOrderBy: (type = '') -> + if !type + @.like_is_open = false + @.activity_is_open = false + + @.onChange({orderBy: type}) + +angular.module("taigaDiscover").controller("DiscoverSearchListHeader", DiscoverSearchListHeaderController) diff --git a/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.spec.coffee b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.spec.coffee new file mode 100644 index 00000000..bda659f9 --- /dev/null +++ b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.controller.spec.coffee @@ -0,0 +1,117 @@ +### +# Copyright (C) 2014-2015 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: discover-search-list-header.controller.spec.coffee +### + +describe "DiscoverSearchListHeader", -> + $provide = null + $controller = null + scope = null + + _inject = -> + inject (_$controller_, $rootScope) -> + $controller = _$controller_ + scope = $rootScope.$new() + + _setup = -> + _inject() + + beforeEach -> + module "taigaDiscover" + + _setup() + + it "openLike", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '' + }) + + ctrl.like_is_open = false + ctrl.activity_is_open = true + ctrl.setOrderBy = sinon.spy() + + ctrl.openLike() + + expect(ctrl.like_is_open).to.be.true + expect(ctrl.activity_is_open).to.be.false + expect(ctrl.setOrderBy).have.been.calledWith('-total_fans_last_week') + + it "openActivity", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '' + }) + + ctrl.activity_is_open = false + ctrl.like_is_open = true + ctrl.setOrderBy = sinon.spy() + + ctrl.openActivity() + + expect(ctrl.activity_is_open).to.be.true + expect(ctrl.like_is_open).to.be.false + expect(ctrl.setOrderBy).have.been.calledWith('-total_activity_last_week') + + it "setOrderBy", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '' + }) + + ctrl.onChange = sinon.spy() + + ctrl.setOrderBy("type1") + + expect(ctrl.onChange).to.have.been.calledWith(sinon.match({orderBy: "type1"})) + + it "setOrderBy falsy close the like or activity layer", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '' + }) + + ctrl.like_is_open = true + ctrl.activity_is_open = true + + ctrl.onChange = sinon.spy() + + ctrl.setOrderBy() + + expect(ctrl.onChange).to.have.been.calledWith(sinon.match({orderBy: ''})) + expect(ctrl.like_is_open).to.be.false + expect(ctrl.activity_is_open).to.be.false + + it "closed like & activity", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '' + }) + + expect(ctrl.like_is_open).to.be.false + expect(ctrl.activity_is_open).to.be.false + + it "open like", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '-total_fans' + }) + + expect(ctrl.like_is_open).to.be.true + expect(ctrl.activity_is_open).to.be.false + + it "open activity", () -> + ctrl = $controller("DiscoverSearchListHeader", scope, { + orderBy: '-total_activity' + }) + + expect(ctrl.like_is_open).to.be.false + expect(ctrl.activity_is_open).to.be.true diff --git a/app/modules/discover/components/discover-search-list-header/discover-search-list-header.directive.coffee b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.directive.coffee new file mode 100644 index 00000000..ce344946 --- /dev/null +++ b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.directive.coffee @@ -0,0 +1,37 @@ +### +# Copyright (C) 2014-2015 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: discover-search-list-header.directive.coffee +### + +DiscoverSearchListHeaderDirective = () -> + link = (scope, el, attrs) -> + + return { + controller: "DiscoverSearchListHeader", + controllerAs: "vm", + bindToController: true, + templateUrl: "discover/components/discover-search-list-header/discover-search-list-header.html", + scope: { + onChange: "&", + orderBy: "=" + }, + link: link + } + +DiscoverSearchListHeaderDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgDiscoverSearchListHeader", DiscoverSearchListHeaderDirective) diff --git a/app/modules/discover/components/discover-search-list-header/discover-search-list-header.jade b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.jade new file mode 100644 index 00000000..b480a619 --- /dev/null +++ b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.jade @@ -0,0 +1,89 @@ +.discover-results-header + .discover-results-header-inner + .title + include ../../../../svg/search.svg + h2 {{ 'DISCOVER.SEARCH.RESULTS' | translate }} + + .filter-discover-search(ng-mouseleave="vm.toggleClose()") + a.discover-search-filter( + href="#" + ng-click="vm.openLike()" + ng-class="{active: vm.like_is_open}" + ) + include ../../../../svg/like.svg + span {{ 'DISCOVER.MOST_LIKED' | translate }} + a.discover-search-filter( + href="#" + ng-click="vm.openActivity()" + ng-class="{active: vm.activity_is_open}" + ) + include ../../../../svg/activity.svg + span {{ 'DISCOVER.MOST_ACTIVE' | translate }} + + .discover-search-subfilter.most-liked-subfilter(ng-if="vm.like_is_open") + a.results( + ng-if="vm.orderBy" + title="" + href="#", + ng-click="vm.setOrderBy()" + ) {{ 'DISCOVER.FILTERS.CLEAR' | translate }} + + ul.filter-list + li + a( + ng-class="{active: vm.orderBy == '-total_fans_last_week'}", + href="#", + ng-click="vm.setOrderBy('-total_fans_last_week')" + ) {{ 'DISCOVER.FILTERS.WEEK' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_fans_last_month'}", + href="#", + ng-click="vm.setOrderBy('-total_fans_last_month')" + ) {{ 'DISCOVER.FILTERS.MONTH' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_fans_last_year'}", + href="#", + ng-click="vm.setOrderBy('-total_fans_last_year')" + ) {{ 'DISCOVER.FILTERS.YEAR' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_fans'}", + href="#", + ng-click="vm.setOrderBy('-total_fans')" + ) {{ 'DISCOVER.FILTERS.ALL_TIME' | translate }} + + .discover-search-subfilter.most-active-subfilter(ng-if="vm.activity_is_open") + a.results( + ng-if="vm.orderBy" + title="" + href="#", + ng-click="vm.setOrderBy()" + ) {{ 'DISCOVER.FILTERS.CLEAR' | translate }} + + ul.filter-list + li + a( + ng-class="{active: vm.orderBy == '-total_activity_last_week'}", + href="#", + ng-click="vm.setOrderBy('-total_activity_last_week')" + ) {{ 'DISCOVER.FILTERS.WEEK' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_activity_last_month'}", + href="#", + ng-click="vm.setOrderBy('-total_activity_last_month')" + ) {{ 'DISCOVER.FILTERS.MONTH' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_activity_last_year'}", + href="#", + ng-click="vm.setOrderBy('-total_activity_last_year')" + ) {{ 'DISCOVER.FILTERS.YEAR' | translate }} + li + a( + ng-class="{active: vm.orderBy == '-total_activity'}", + href="#", + ng-click="vm.setOrderBy('-total_activity')" + ) {{ 'DISCOVER.FILTERS.ALL_TIME' | translate }} diff --git a/app/modules/discover/components/discover-search-list-header/discover-search-list-header.scss b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.scss new file mode 100644 index 00000000..d3112c54 --- /dev/null +++ b/app/modules/discover/components/discover-search-list-header/discover-search-list-header.scss @@ -0,0 +1,80 @@ +.discover-results-header { + .discover-results-header-inner { + align-items: center; + display: flex; + justify-content: space-between; + } + svg { + @include svg-size(1.1rem); + fill: $gray-light; + } + .title { + @extend %bold; + @extend %larger; + text-transform: uppercase; + } + h2 { + display: inline-block; + } +} + +.filter-discover-search { + .discover-search-filter { + margin-right: 1rem; + &.active { + color: $primary; + } + } +} + +.discover-search-subfilter { + @include arrow('bottom', $whitish, $whitish, 1, 8); + align-items: center; + background: $whitish; + display: flex; + justify-content: space-between; + position: relative; + &.most-liked-subfilter { + &::after, + &::before { + left: 85%; + } + } + &.most-active-subfilter { + &::after, + &::before { + left: 95%; + } + } + &.ng-enter { + animation: dropdownFade .2s; + } + .results { + @extend %small; + color: $red-light; + display: block; + padding: .5rem 1rem; + transition: all .2s; + &:hover { + color: $red; + } + } + .filter-list { + display: flex; + margin: 0; + margin-left: auto; + a { + display: block; + padding: .5rem 1rem; + transition: all .2s; + &:hover { + background: $gray-light; + color: currentColor; + } + &.active { + background: $primary-light; + color: $whitish; + } + } + } +} diff --git a/app/modules/discover/components/featured-projects/featured-projects.controller.coffee b/app/modules/discover/components/featured-projects/featured-projects.controller.coffee new file mode 100644 index 00000000..9175a524 --- /dev/null +++ b/app/modules/discover/components/featured-projects/featured-projects.controller.coffee @@ -0,0 +1,30 @@ +### +# Copyright (C) 2014-2015 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: featured-projects.controller.coffee +### + +class FeaturedProjectsController + @.$inject = [ + "tgDiscoverProjectsService" + ] + + constructor: (@discoverProjectsService) -> + taiga.defineImmutableProperty @, "featured", () => return @discoverProjectsService.featured + + @discoverProjectsService.fetchFeatured() + +angular.module("taigaDiscover").controller("FeaturedProjects", FeaturedProjectsController) diff --git a/app/modules/discover/components/featured-projects/featured-projects.directive.coffee b/app/modules/discover/components/featured-projects/featured-projects.directive.coffee new file mode 100644 index 00000000..0ec079b5 --- /dev/null +++ b/app/modules/discover/components/featured-projects/featured-projects.directive.coffee @@ -0,0 +1,33 @@ +### +# Copyright (C) 2014-2015 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: featured-projects.directive.coffee +### + +FeaturedProjectsDirective = () -> + link = (scope, el, attrs) -> + + return { + controller: "FeaturedProjects" + controllerAs: "vm", + templateUrl: "discover/components/featured-projects/featured-projects.html", + scope: {}, + link: link + } + +FeaturedProjectsDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgFeaturedProjects", FeaturedProjectsDirective) diff --git a/app/modules/discover/components/featured-projects/featured-projects.jade b/app/modules/discover/components/featured-projects/featured-projects.jade new file mode 100644 index 00000000..ec1de366 --- /dev/null +++ b/app/modules/discover/components/featured-projects/featured-projects.jade @@ -0,0 +1,52 @@ +.featured-projects(ng-if="vm.featured.size") + h1.title {{ 'DISCOVER.FEATURED' | translate }} + + .featured-projects-inner + .featured-project(tg-repeat="project in vm.featured track by project.get('id')") + .tags-container + .project-tag( + style="background: {{tag.get('color')}}" + title="{{tag.get('name')}}" + tg-repeat="tag in project.get('colorized_tags') track by tag.get('name')" + ) + .project-card-inner + .project-card-header + a.project-card-logo( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + h2.project-card-name + a( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) {{::project.get('name')}} + span.look-for-people( + ng-if="project.get('is_looking_for_people')" + title="{{ ::project.get('looking_for_people_note') }}" + ) + include ../../../../svg/recruit.svg + p.project-card-description {{ ::project.get('description') | limitTo:100 }}{{ ::project.get('description').length < 100 ? '' : '...'}} + .project-card-statistics + span.statistic( + ng-class="{'active': project.get('is_fan')}" + title="{{ 'PROJECT.FANS_COUNTER_TITLE'|translate:{total:project.get('total_fans')||0}:'messageformat' }}" + ) + include ../../../../svg/like.svg + span {{::project.get('total_fans')}} + span.statistic( + ng-class="{'active': project.get('is_watcher')}" + title="{{ 'PROJECT.WATCHERS_COUNTER_TITLE'|translate:{total:project.get('total_watchers')||0}:'messageformat' }}" + ) + include ../../../../svg/eye.svg + span {{::project.get('total_watchers')}} + span.statistic( + title="{{ 'PROJECT.MEMBERS_COUNTER_TITLE'|translate:{total:project.get('members').size||0}:'messageformat' }}" + ) + include ../../../../svg/team.svg + span.statistics-num {{ ::project.get('members').size }} diff --git a/app/modules/discover/components/featured-projects/featured-projects.scss b/app/modules/discover/components/featured-projects/featured-projects.scss new file mode 100644 index 00000000..2efdce61 --- /dev/null +++ b/app/modules/discover/components/featured-projects/featured-projects.scss @@ -0,0 +1,32 @@ +@import '../../../../styles/dependencies/mixins/project-card'; + +.featured-projects { + @include centered; + .title { + @extend %bold; + @extend %larger; + color: $grayer; + text-align: center; + } +} +.featured-projects-inner { + align-items: stretch; + display: flex; + flex-grow: 0; + flex-wrap: wrap; + justify-content: space-between; +} + +.featured-project { + @include project-card; + display: flex; + flex-basis: 23%; + flex-wrap: wrap; + max-width: 23%; + @include breakpoint(tablet) { + flex-basis: 45%; + } + @include breakpoint(mobile) { + flex-basis: 100%; + } +} diff --git a/app/modules/discover/components/highlighted/highlighted.directive.coffee b/app/modules/discover/components/highlighted/highlighted.directive.coffee new file mode 100644 index 00000000..3fee80b4 --- /dev/null +++ b/app/modules/discover/components/highlighted/highlighted.directive.coffee @@ -0,0 +1,32 @@ +### +# Copyright (C) 2014-2015 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: highlighted.directive.coffee +### + +HighlightedDirective = () -> + return { + templateUrl: "discover/components/highlighted/highlighted.html", + scope: { + loading: "=", + highlighted: "=", + orderBy: "=" + } + } + +HighlightedDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgHighlighted", HighlightedDirective) diff --git a/app/modules/discover/components/highlighted/highlighted.jade b/app/modules/discover/components/highlighted/highlighted.jade new file mode 100644 index 00000000..a7606e77 --- /dev/null +++ b/app/modules/discover/components/highlighted/highlighted.jade @@ -0,0 +1,57 @@ +.highlighted-projects-container + .loading-container( + tg-loading="loading" + ng-show="loading" + ) + .highlighted-project( + tg-repeat="project in highlighted track by project.get('id')" + ng-if="!loading" + ) + a.project-logo( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + .project-data-container + .single-project-header + h2.project-title + a( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) {{::project.get('name')}} + span.look-for-people( + ng-if="project.get('is_looking_for_people')" + title="{{ ::project.get('looking_for_people_note') }}" + ) + include ../../../../svg/recruit.svg + .project-statistics + span.statistic( + ng-class="{'active': project.get('is_fan')}" + title="{{ 'PROJECT.FANS_COUNTER_TITLE'|translate:{total:project.get('total_fans')||0}:'messageformat' }}" + ) + include ../../../../svg/like.svg + span {{::project.get('total_fans')}} + span.statistic( + ng-class="{'active': project.get('is_watcher')}" + title="{{ 'PROJECT.WATCHERS_COUNTER_TITLE'|translate:{total:project.get('total_watchers')||0}:'messageformat' }}" + ) + include ../../../../svg/eye.svg + span {{::project.get('total_watchers')}} + span.statistic( + title="{{ 'PROJECT.MEMBERS_COUNTER_TITLE'|translate:{total:project.get('members').size||0}:'messageformat' }}" + ) + include ../../../../svg/team.svg + span.statistics-num {{ ::project.get('members').size }} + p.project-description {{ ::project.get('description') | limitTo:150 }}{{ ::project.get('description').length < 150 ? '' : '...'}} + + a.view-more-projects.button-green( + ng-if="highlighted" + tg-nav="discover-search" + tg-nav-get-params="{\"order_by\": \"{{orderBy}}\"}" + href="#" + ) {{ 'DISCOVER.VIEW_MORE' | translate }} diff --git a/app/modules/discover/components/highlighted/highlighted.scss b/app/modules/discover/components/highlighted/highlighted.scss new file mode 100644 index 00000000..fb2091b9 --- /dev/null +++ b/app/modules/discover/components/highlighted/highlighted.scss @@ -0,0 +1,201 @@ +.highlighted { + @include centered; + display: flex; + justify-content: space-around; + margin-bottom: 4rem; + @include breakpoint(tablet) { + flex-direction: column; + tg-most-active { + margin-top: 4rem; + } + } + tg-most-liked, + tg-most-active { + align-content: stretch; + display: flex; + flex: 1; + } + tg-most-liked { + margin-right: 8%; + @include breakpoint(tablet) { + margin-right: 0; + } + } + .most-active, + .most-liked { + align-content: stretch; + display: flex; + flex: 1; + flex-direction: column; + } + .header { + align-items: center; + display: flex; + justify-content: space-between; + margin-bottom: 1rem; + svg { + @include svg-size(1.5rem); + fill: $gray-light; + margin: .5rem; + } + } + .title-wrapper { + align-items: center; + display: flex; + } + .title { + @extend %bold; + @extend %larger; + color: $grayer; + display: inline-block; + margin: 0; + } + .highlighted-projects-container { + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-start; + } + .loading-container { + margin-top: calc(50% - 1rem); + } + .loading-spinner { + display: block; + margin: 2rem auto; + max-height: 3rem; + max-width: 3rem; + } + .view-more-projects { + margin-top: auto; + width: 100%; + } + .empty-highlighted-project { + border: 2px dashed $whitish; + padding: 2rem; + text-align: center; + svg { + @include svg-size(2rem); + display: block; + fill: $gray-light; + margin: 1rem auto; + } + span { + @extend %light; + color: $gray; + display: block; + } + } +} + + +.filter-highlighted { + position: relative; + .current-filter { + padding: 1rem; + span { + margin-left: .2rem; + position: relative; + top: .2rem; + } + } + .filter-list { + background: $black; + position: absolute; + right: 0; + top: 1.5rem; + &.ng-enter { + animation: dropdownFade .2s ease-in; + } + &.ng-leave { + animation: dropdownFade .2s ease-in; + animation-direction: reverse; + } + } + li { + @extend %small; + color: $white; + cursor: pointer; + min-width: 8rem; + padding: .25rem .5rem; + &:hover { + background: rgba($primary-light, .4); + } + } +} + +.highlighted-project { + align-items: flex-start; + border-bottom: 1px solid $whitish; + display: flex; + flex-basis: 9rem; + min-height: 9rem; + padding: 1.5rem 0; + &:nth-last-child(-n+2) { + border-bottom: 0; + } + .project-logo { + flex-basis: 3rem; + height: auto; + margin-right: 1rem; + width: 3rem; + img { + width: 100%; + } + } + .project-data-container { + flex: 1; + } + .single-project-header { + align-content: center; + display: flex; + justify-content: space-between; + } + .project-title { + @extend %large; + @extend %text; + display: inline-block; + margin-bottom: .5rem; + a { + color: $primary; + &:hover { + color: $primary-light; + } + } + } + .look-for-people { + svg { + @include svg-size(); + fill: $gray-light; + margin-left: .5rem; + } + } + .project-description { + @extend %small; + color: $gray; + margin-bottom: 0; + } + .project-statistics { + display: flex; + flex-basis: 140px; + justify-content: flex-end; + svg { + @include svg-size(.8rem); + fill: $gray-light; + } + .svg-eye-closed { + display: none; + } + } + .statistic { + @extend %small; + color: $gray-light; + display: inline-block; + margin-right: .5rem; + &.active { + color: $primary; + svg { + fill: $primary; + } + } + } +} diff --git a/app/modules/discover/components/most-active/most-active.controller.coffee b/app/modules/discover/components/most-active/most-active.controller.coffee new file mode 100644 index 00000000..6c8676b7 --- /dev/null +++ b/app/modules/discover/components/most-active/most-active.controller.coffee @@ -0,0 +1,49 @@ +### +# Copyright (C) 2014-2015 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: most-active.controller.coffee +### + +class MostActiveController + @.$inject = [ + "tgDiscoverProjectsService" + ] + + constructor: (@discoverProjectsService) -> + taiga.defineImmutableProperty @, "highlighted", () => return @discoverProjectsService.mostActive + + @.currentOrderBy = 'week' + @.order_by = @.getOrderBy() + + fetch: () -> + @.loading = true + @.order_by = @.getOrderBy() + + return @discoverProjectsService.fetchMostActive({order_by: @.order_by}).then () => + @.loading = false + + orderBy: (type) -> + @.currentOrderBy = type + + @.fetch() + + getOrderBy: (type) -> + if @.currentOrderBy == 'all' + return '-total_activity' + else + return '-total_activity_last_' + @.currentOrderBy + +angular.module("taigaDiscover").controller("MostActive", MostActiveController) diff --git a/app/modules/discover/components/most-active/most-active.controller.spec.coffee b/app/modules/discover/components/most-active/most-active.controller.spec.coffee new file mode 100644 index 00000000..da7258bc --- /dev/null +++ b/app/modules/discover/components/most-active/most-active.controller.spec.coffee @@ -0,0 +1,79 @@ +### +# Copyright (C) 2014-2015 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: most-active.controller.spec.coffee +### + +describe "MostActive", -> + $provide = null + $controller = null + mocks = {} + + _mockDiscoverProjectsService = -> + mocks.discoverProjectsService = { + fetchMostActive: sinon.stub() + } + + $provide.value("tgDiscoverProjectsService", mocks.discoverProjectsService) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockDiscoverProjectsService() + + return null + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaDiscover" + + _setup() + + it "fetch", (done) -> + ctrl = $controller("MostActive") + + ctrl.getOrderBy = sinon.stub().returns('week') + + mockPromise = mocks.discoverProjectsService.fetchMostActive.withArgs(sinon.match({order_by: 'week'})).promise() + + promise = ctrl.fetch() + + expect(ctrl.loading).to.be.true + + mockPromise.resolve() + + promise.finally () -> + expect(ctrl.loading).to.be.false + done() + + + it "order by", () -> + ctrl = $controller("MostActive") + + ctrl.fetch = sinon.spy() + + ctrl.orderBy('month') + + expect(ctrl.fetch).to.have.been.called + expect(ctrl.currentOrderBy).to.be.equal('month') diff --git a/app/modules/discover/components/most-active/most-active.directive.coffee b/app/modules/discover/components/most-active/most-active.directive.coffee new file mode 100644 index 00000000..c3cf6b84 --- /dev/null +++ b/app/modules/discover/components/most-active/most-active.directive.coffee @@ -0,0 +1,34 @@ +### +# Copyright (C) 2014-2015 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: most-active.directive.coffee +### + +MostActiveDirective = () -> + link = (scope, el, attrs, ctrl) -> + ctrl.fetch() + + return { + controller: "MostActive" + controllerAs: "vm", + templateUrl: "discover/components/most-active/most-active.html", + scope: {}, + link: link + } + +MostActiveDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgMostActive", MostActiveDirective) diff --git a/app/modules/discover/components/most-active/most-active.jade b/app/modules/discover/components/most-active/most-active.jade new file mode 100644 index 00000000..37e58cdf --- /dev/null +++ b/app/modules/discover/components/most-active/most-active.jade @@ -0,0 +1,18 @@ +.most-active(ng-if="vm.highlighted.size") + .header + .title-wrapper + include ../../../../svg/activity.svg + h1.title {{ 'DISCOVER.MOST_ACTIVE' | translate }} + tg-discover-home-order-by(on-change="vm.orderBy(orderBy)", order-by="vm.currentOrderBy") + + tg-highlighted( + loading="vm.loading", + highlighted="vm.highlighted" + order-by="vm.order_by" + ) + +.empty-highlighted-project( + ng-if="!vm.highlighted.size" +) + include ../../../../svg/activity.svg + span {{ 'DISCOVER.MOST_ACTIVE_EMPTY' | translate }} diff --git a/app/modules/discover/components/most-liked/most-liked.controller.coffee b/app/modules/discover/components/most-liked/most-liked.controller.coffee new file mode 100644 index 00000000..b75e2c9a --- /dev/null +++ b/app/modules/discover/components/most-liked/most-liked.controller.coffee @@ -0,0 +1,49 @@ +### +# Copyright (C) 2014-2015 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: msot-liked.controller.coffee +### + +class MostLikedController + @.$inject = [ + "tgDiscoverProjectsService" + ] + + constructor: (@discoverProjectsService) -> + taiga.defineImmutableProperty @, "highlighted", () => return @discoverProjectsService.mostLiked + + @.currentOrderBy = 'week' + @.order_by = @.getOrderBy() + + fetch: () -> + @.loading = true + @.order_by = @.getOrderBy() + + @discoverProjectsService.fetchMostLiked({order_by: @.order_by}).then () => + @.loading = false + + orderBy: (type) -> + @.currentOrderBy = type + + @.fetch() + + getOrderBy: () -> + if @.currentOrderBy == 'all' + return '-total_fans' + else + return '-total_fans_last_' + @.currentOrderBy + +angular.module("taigaDiscover").controller("MostLiked", MostLikedController) diff --git a/app/modules/discover/components/most-liked/most-liked.controller.spec.coffee b/app/modules/discover/components/most-liked/most-liked.controller.spec.coffee new file mode 100644 index 00000000..f00ee3ad --- /dev/null +++ b/app/modules/discover/components/most-liked/most-liked.controller.spec.coffee @@ -0,0 +1,79 @@ +### +# Copyright (C) 2014-2015 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: most-liked.controller.spec.coffee +### + +describe "MostLiked", -> + $provide = null + $controller = null + mocks = {} + + _mockDiscoverProjectsService = -> + mocks.discoverProjectsService = { + fetchMostLiked: sinon.stub() + } + + $provide.value("tgDiscoverProjectsService", mocks.discoverProjectsService) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockDiscoverProjectsService() + + return null + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaDiscover" + + _setup() + + it "fetch", (done) -> + ctrl = $controller("MostLiked") + + ctrl.getOrderBy = sinon.stub().returns('week') + + mockPromise = mocks.discoverProjectsService.fetchMostLiked.withArgs(sinon.match({order_by: 'week'})).promise() + + promise = ctrl.fetch() + + expect(ctrl.loading).to.be.true + + mockPromise.resolve() + + promise.finally () -> + expect(ctrl.loading).to.be.false + done() + + + it "order by", () -> + ctrl = $controller("MostLiked") + + ctrl.fetch = sinon.spy() + + ctrl.orderBy('month') + + expect(ctrl.fetch).to.have.been.called + expect(ctrl.currentOrderBy).to.be.equal('month') diff --git a/app/modules/discover/components/most-liked/most-liked.directive.coffee b/app/modules/discover/components/most-liked/most-liked.directive.coffee new file mode 100644 index 00000000..06813cb9 --- /dev/null +++ b/app/modules/discover/components/most-liked/most-liked.directive.coffee @@ -0,0 +1,34 @@ +### +# Copyright (C) 2014-2015 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: most-liked.directive.coffee +### + +MostLikedDirective = () -> + link = (scope, el, attrs, ctrl) -> + ctrl.fetch() + + return { + controller: "MostLiked" + controllerAs: "vm", + templateUrl: "discover/components/most-liked/most-liked.html", + scope: {}, + link: link + } + +MostLikedDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgMostLiked", MostLikedDirective) diff --git a/app/modules/discover/components/most-liked/most-liked.jade b/app/modules/discover/components/most-liked/most-liked.jade new file mode 100644 index 00000000..e3967aa0 --- /dev/null +++ b/app/modules/discover/components/most-liked/most-liked.jade @@ -0,0 +1,17 @@ +.most-liked(ng-if="vm.highlighted.size") + .header + .title-wrapper + include ../../../../svg/like.svg + h1.title {{ 'DISCOVER.MOST_LIKED' | translate }} + tg-discover-home-order-by(on-change="vm.orderBy(orderBy)", order-by="vm.currentOrderBy") + tg-highlighted( + loading="vm.loading", + highlighted="vm.highlighted" + order-by="vm.order_by" + ) + +.empty-highlighted-project( + ng-if="!vm.highlighted.size" +) + include ../../../../svg/like.svg + span {{ 'DISCOVER.MOST_LIKED_EMPTY' | translate }} diff --git a/app/modules/discover/discover-home/discover-home.controller.coffee b/app/modules/discover/discover-home/discover-home.controller.coffee new file mode 100644 index 00000000..d9d249d6 --- /dev/null +++ b/app/modules/discover/discover-home/discover-home.controller.coffee @@ -0,0 +1,33 @@ +### +# Copyright (C) 2014-2015 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: discover-home.controller.coffee +### + +class DiscoverHomeController + @.$inject = [ + '$tgLocation', + '$tgNavUrls' + ] + + constructor: (@location, @navUrls) -> + + onSubmit: (q) -> + url = @navUrls.resolve('discover-search') + + @location.search('text', q).path(url) + +angular.module("taigaDiscover").controller("DiscoverHome", DiscoverHomeController) diff --git a/app/modules/discover/discover-home/discover-home.controller.spec.coffee b/app/modules/discover/discover-home/discover-home.controller.spec.coffee new file mode 100644 index 00000000..0318c6ba --- /dev/null +++ b/app/modules/discover/discover-home/discover-home.controller.spec.coffee @@ -0,0 +1,71 @@ +### +# Copyright (C) 2014-2015 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: doscover-home.controller.spec.coffee +### + +describe "DiscoverHomeController", -> + $provide = null + $controller = null + mocks = {} + + _mockLocation = -> + mocks.location = {} + + $provide.value('$tgLocation', mocks.location) + + _mockNavUrls = -> + mocks.navUrls = {} + + $provide.value('$tgNavUrls', mocks.navUrls) + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockLocation() + _mockNavUrls() + + return null + + _setup = -> + _inject() + + beforeEach -> + module "taigaDiscover" + + _mocks() + _setup() + + it "onSubmit redirect to discover search", () -> + mocks.navUrls.resolve = sinon.stub().withArgs('discover-search').returns('url') + + pathSpy = sinon.spy() + searchStub = { + path: pathSpy + } + + mocks.location.search = sinon.stub().withArgs('text', 'query').returns(searchStub) + + ctrl = $controller("DiscoverHome") + + ctrl.onSubmit('query') + + expect(pathSpy).to.have.been.calledWith('url'); diff --git a/app/modules/discover/discover-home/discover-home.jade b/app/modules/discover/discover-home/discover-home.jade new file mode 100644 index 00000000..56096cb8 --- /dev/null +++ b/app/modules/discover/discover-home/discover-home.jade @@ -0,0 +1,12 @@ +doctype html + +section.discover + header + tg-discover-search-bar(on-change="vm.onSubmit(q)") + + section.highlighted + tg-most-liked + tg-most-active + + section.featured-projects + tg-featured-projects diff --git a/app/modules/discover/discover-search/discover-search.controller.coffee b/app/modules/discover/discover-search/discover-search.controller.coffee new file mode 100644 index 00000000..0744a9ad --- /dev/null +++ b/app/modules/discover/discover-search/discover-search.controller.coffee @@ -0,0 +1,114 @@ +### +# Copyright (C) 2014-2015 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: discover-search.controller.coffee +### + +class DiscoverSearchController + @.$inject = [ + '$routeParams', + 'tgDiscoverProjectsService', + '$route' + ] + + constructor: (@routeParams, @discoverProjectsService, @route) -> + @.page = 1 + + taiga.defineImmutableProperty @, "searchResult", () => return @discoverProjectsService.searchResult + taiga.defineImmutableProperty @, "nextSearchPage", () => return @discoverProjectsService.nextSearchPage + + @.q = @routeParams.text + @.filter = @routeParams.filter || 'all' + @.orderBy = @routeParams['order_by'] || '' + + @.loadingGlobal = false + @.loadingList = false + @.loadingPagination = false + + fetch: () -> + @.page = 1 + + @discoverProjectsService.resetSearchList() + + return @.search() + + fetchByGlobalSearch: () -> + return if @.loadingGlobal + + @.loadingGlobal = true + + @.fetch().then () => @.loadingGlobal = false + + fetchByOrderBy: () -> + return if @.loadingList + + @.loadingList = true + + @.fetch().then () => @.loadingList = false + + showMore: () -> + return if @.loadingPagination + + @.loadingPagination = true + + @.page++ + + return @.search().then () => @.loadingPagination = false + + search: () -> + filter = @.getFilter() + + params = { + page: @.page, + q: @.q, + order_by: @.orderBy + } + + _.assign(params, filter) + + return @discoverProjectsService.fetchSearch(params) + + getFilter: () -> + if @.filter == 'people' + return {is_looking_for_people: true} + else if @.filter == 'scrum' + return {is_backlog_activated: true} + else if @.filter == 'kanban' + return {is_kanban_activated: true} + + return {} + + onChangeFilter: (filter, q) -> + @.filter = filter + @.q = q + + @route.updateParams({ + filter: @.filter, + text: @.q + }) + + @.fetchByGlobalSearch() + + onChangeOrder: (orderBy) -> + @.orderBy = orderBy + + @route.updateParams({ + order_by: orderBy + }) + + @.fetchByOrderBy() + +angular.module("taigaDiscover").controller("DiscoverSearch", DiscoverSearchController) diff --git a/app/modules/discover/discover-search/discover-search.controller.spec.coffee b/app/modules/discover/discover-search/discover-search.controller.spec.coffee new file mode 100644 index 00000000..6c05d661 --- /dev/null +++ b/app/modules/discover/discover-search/discover-search.controller.spec.coffee @@ -0,0 +1,199 @@ +### +# Copyright (C) 2014-2015 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: discover-search.controller.spec.coffee +### + +describe "DiscoverSearch", -> + $provide = null + $controller = null + mocks = {} + + _mockRouteParams = -> + mocks.routeParams = {} + + $provide.value("$routeParams", mocks.routeParams) + + _mockRoute = -> + mocks.route = {} + + $provide.value("$route", mocks.route) + + _mockDiscoverProjects = -> + mocks.discoverProjects = { + resetSearchList: sinon.spy(), + fetchSearch: sinon.stub() + } + + mocks.discoverProjects.fetchSearch.promise().resolve() + + $provide.value("tgDiscoverProjectsService", mocks.discoverProjects) + + _mocks = -> + module (_$provide_) -> + $provide = _$provide_ + + _mockRoute() + _mockRouteParams() + _mockDiscoverProjects() + + return null + + _inject = -> + inject (_$controller_) -> + $controller = _$controller_ + + _setup = -> + _mocks() + _inject() + + beforeEach -> + module "taigaDiscover" + + _setup() + + it "initialize search params", () -> + mocks.routeParams.text = 'text' + mocks.routeParams.filter = 'filter' + mocks.routeParams.order_by = 'order' + + ctrl = $controller('DiscoverSearch') + + expect(ctrl.q).to.be.equal('text') + expect(ctrl.filter).to.be.equal('filter') + expect(ctrl.orderBy).to.be.equal('order') + + it "fetch", () -> + ctrl = $controller('DiscoverSearch') + + ctrl.search = sinon.spy() + + ctrl.fetch() + + expect(mocks.discoverProjects.resetSearchList).to.have.been.called + expect(ctrl.search).to.have.been.called + expect(ctrl.page).to.be.equal(1) + + it "showMore", (done) -> + ctrl = $controller('DiscoverSearch') + + ctrl.search = sinon.stub().promise() + + ctrl.showMore().then () -> + expect(ctrl.loadingPagination).to.be.false + + done() + + expect(ctrl.loadingPagination).to.be.true + expect(ctrl.search).to.have.been.called + expect(ctrl.page).to.be.equal(2) + + ctrl.search.resolve() + + it "search", () -> + mocks.discoverProjects.fetchSearch = sinon.stub() + + filter = { + filter: '123' + } + + ctrl = $controller('DiscoverSearch') + + ctrl.page = 1 + ctrl.q = 'text' + ctrl.orderBy = 1 + + ctrl.getFilter = () -> return filter + + params = { + filter: '123', + page: 1, + q: 'text', + order_by: 1 + } + + ctrl.search() + + expect(mocks.discoverProjects.fetchSearch).have.been.calledWith(sinon.match(params)) + + it "get filter", () -> + ctrl = $controller('DiscoverSearch') + + ctrl.filter = 'people' + expect(ctrl.getFilter()).to.be.eql({is_looking_for_people: true}) + + ctrl.filter = 'scrum' + expect(ctrl.getFilter()).to.be.eql({is_backlog_activated: true}) + + ctrl.filter = 'kanban' + expect(ctrl.getFilter()).to.be.eql({is_kanban_activated: true}) + + it "onChangeFilter", () -> + ctrl = $controller('DiscoverSearch') + + mocks.route.updateParams = sinon.stub() + + ctrl.fetchByGlobalSearch = sinon.spy() + + ctrl.onChangeFilter('filter', 'query') + + expect(ctrl.filter).to.be.equal('filter') + expect(ctrl.q).to.be.equal('query') + expect(ctrl.fetchByGlobalSearch).to.have.been.called + expect(mocks.route.updateParams).to.have.been.calledWith(sinon.match({filter: 'filter', text: 'query'})) + + it "onChangeOrder", () -> + ctrl = $controller('DiscoverSearch') + + mocks.route.updateParams = sinon.stub() + + ctrl.fetchByOrderBy = sinon.spy() + + ctrl.onChangeOrder('order-by') + + expect(ctrl.orderBy).to.be.equal('order-by') + expect(ctrl.fetchByOrderBy).to.have.been.called + expect(mocks.route.updateParams).to.have.been.calledWith(sinon.match({order_by: 'order-by'})) + + it "fetchByGlobalSearch", (done) -> + ctrl = $controller('DiscoverSearch') + + ctrl.fetch = sinon.stub().promise() + + ctrl.fetchByGlobalSearch().then () -> + expect(ctrl.loadingGlobal).to.be.false + + done() + + expect(ctrl.loadingGlobal).to.be.true + expect(ctrl.fetch).to.have.been.called + + ctrl.fetch.resolve() + + it "fetchByOrderBy", (done) -> + ctrl = $controller('DiscoverSearch') + + ctrl.fetch = sinon.stub().promise() + + ctrl.fetchByOrderBy().then () -> + expect(ctrl.loadingList).to.be.false + + done() + + expect(ctrl.loadingList).to.be.true + expect(ctrl.fetch).to.have.been.called + + ctrl.fetch.resolve() diff --git a/app/modules/discover/discover-search/discover-search.directive.coffee b/app/modules/discover/discover-search/discover-search.directive.coffee new file mode 100644 index 00000000..5f66cbea --- /dev/null +++ b/app/modules/discover/discover-search/discover-search.directive.coffee @@ -0,0 +1,32 @@ +### +# Copyright (C) 2014-2016 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: discover-search.directive.coffee +### + +DiscoverSearchDirective = () -> + link = (scope, element, attrs, ctrl) -> + ctrl.fetch() + + return { + controller: "DiscoverSearch", + controllerAs: "vm" + link: link + } + +DiscoverSearchDirective.$inject = [] + +angular.module("taigaDiscover").directive("tgDiscoverSearch", DiscoverSearchDirective) diff --git a/app/modules/discover/discover-search/discover-search.jade b/app/modules/discover/discover-search/discover-search.jade new file mode 100644 index 00000000..e7629e0b --- /dev/null +++ b/app/modules/discover/discover-search/discover-search.jade @@ -0,0 +1,77 @@ +div(tg-discover-search) + .discover-search + tg-discover-search-bar( + filter="vm.filter", + q="vm.q", + on-change="vm.onChangeFilter(filter, q)" + ) + + .empty-discover-results(ng-if="!vm.searchResult.size && !vm.loadingGlobal && !vm.loadingList") + img( + src="/#{v}/images/issues-empty.png", + alt="{{ DISCOVER.EMPTY | translate }}" + ) + p.title(translate="DISCOVER.EMPTY") + + .discover-results(ng-if="vm.searchResult.size || vm.loadingGlobal || vm.loadingList") + .spin(tg-loading="vm.loadingGlobal") + + .discover-results-inner(ng-if="!vm.loadingGlobal") + tg-discover-search-list-header( + on-change="vm.onChangeOrder(orderBy)", + order-by="vm.orderBy" + ) + + .spin(ng-show="vm.loadingList", tg-loading="vm.loadingList") + + ul.project-list(ng-if="!vm.loadingList && vm.searchResult.size") + li.list-itemtype-project(tg-repeat="project in vm.searchResult track by project.get('id')") + .list-itemtype-project-left + a.list-itemtype-project-image( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{ ::project.get('name') }}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + .list-itemtype-project-data + h2 + a( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{ ::project.get('name') }}" + ) {{project.get('name')}} + span.look-for-people( + ng-if="project.get('is_looking_for_people')" + title="{{ ::project.get('looking_for_people_note') }}" + ) + include ../../../svg/recruit.svg + p {{ ::project.get('description') | limitTo:300 }} + span(ng-if="::project.get('description').length > 300") ... + .list-itemtype-project-right.project-statistics + span.statistic( + ng-class="{'active': project.get('is_fan')}" + title="{{ 'PROJECT.FANS_COUNTER_TITLE'|translate:{total:project.get('total_fans')||0}:'messageformat' }}" + ) + include ../../../svg/like.svg + span {{::project.get('total_fans')}} + span.statistic( + ng-class="{'active': project.get('is_watcher')}" + title="{{ 'PROJECT.WATCHERS_COUNTER_TITLE'|translate:{total:project.get('total_watchers')||0}:'messageformat' }}" + ) + include ../../../svg/eye.svg + span {{::project.get('total_watchers')}} + span.statistic( + title="{{ 'PROJECT.MEMBERS_COUNTER_TITLE'|translate:{total:project.get('members').size||0}:'messageformat' }}" + ) + include ../../../svg/team.svg + span.statistics-num {{ ::project.get('members').size }} + + a.button-green.more-results( + tg-loading="vm.loadingPagination" + href="#" + ng-click="vm.showMore()" + ng-if="vm.nextSearchPage" + ) {{ 'DISCOVER.VIEW_MORE' | translate }} diff --git a/app/modules/discover/discover-search/discover-search.scss b/app/modules/discover/discover-search/discover-search.scss new file mode 100644 index 00000000..3e27631c --- /dev/null +++ b/app/modules/discover/discover-search/discover-search.scss @@ -0,0 +1,132 @@ +.discover-search { + .discover-header { + form { + margin: 0 8rem; + position: relative; + } + + .search-button { + left: 1rem; + right: auto; + } + .searchbox { + input { + padding-left: 3.5rem; + padding-right: 23rem; + } + } + } + .searchbox-filters { + position: absolute; + right: 1rem; + top: .7rem; + width: auto; + input { + display: none; + } + label { + border-radius: 4px; + color: $gray-light; + cursor: pointer; + display: inline-block; + padding: .4rem .75rem; + transition: all .2s; + transition-delay: .2s; + &.active { + background: $primary-light; + color: $white; + } + &:hover { + background: $whitish; + color: $gray; + } + } + } +} + +.discover-results { + @include centered; + .discover-results-inner { + .spin { + margin-top: 4rem; + } + } + .list-itemtype-project { + border-bottom: 1px solid $gray-light; + display: flex; + padding: 1rem 0; + &:last-child { + border-bottom: 0; + } + } + .list-itemtype-project-left { + align-items: flex-start; + display: flex; + width: 100%; + } + .list-itemtype-project-image { + flex-shrink: 0; + margin-right: 1rem; + } + .list-itemtype-project-data { + flex: 1; + vertical-align: middle; + } + .look-for-people { + margin-left: .5rem; + svg { + @include svg-size(1rem); + fill: $gray-light; + } + } + .project-statistics { + display: flex; + flex-basis: 140px; + justify-content: flex-end; + svg { + @include svg-size(.8rem); + fill: $gray-light; + } + .svg-eye-closed { + display: none; + } + } + .statistic { + @extend %small; + color: $gray-light; + display: inline-block; + margin-right: .5rem; + &.active { + color: $primary; + svg { + fill: $primary; + } + } + } + .more-results { + display: block; + margin: 0 20rem; + transition: inherit; + } + div[tg-loading] { + img { + display: block; + margin: 0 auto; + } + } +} + +.empty-discover-results { + @include centered; + margin-top: 4rem; + text-align: center; + img { + margin-bottom: 1rem; + } + .title { + @extend %large; + @extend %light; + margin: 0; + text-transform: uppercase; + } +} diff --git a/app/modules/discover/discover.module.coffee b/app/modules/discover/discover.module.coffee new file mode 100644 index 00000000..2c2bfb7f --- /dev/null +++ b/app/modules/discover/discover.module.coffee @@ -0,0 +1,20 @@ +### +# Copyright (C) 2014-2015 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: discover.module.coffee +### + +module = angular.module("taigaDiscover", []) diff --git a/app/modules/discover/services/discover-projects.service.coffee b/app/modules/discover/services/discover-projects.service.coffee new file mode 100644 index 00000000..f53808e3 --- /dev/null +++ b/app/modules/discover/services/discover-projects.service.coffee @@ -0,0 +1,93 @@ +### +# Copyright (C) 2014-2015 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: discover-projects.service.coffee +### + +taiga = @.taiga + +class DiscoverProjectsService extends taiga.Service + @.$inject = [ + "tgResources", + "tgProjectsService" + ] + + constructor: (@rs, @projectsService) -> + @._mostLiked = Immutable.List() + @._mostActive = Immutable.List() + @._featured = Immutable.List() + @._searchResult = Immutable.List() + @._projectsCount = 0 + + @.decorate = @projectsService._decorate.bind(@projectsService) + + taiga.defineImmutableProperty @, "mostLiked", () => return @._mostLiked + taiga.defineImmutableProperty @, "mostActive", () => return @._mostActive + taiga.defineImmutableProperty @, "featured", () => return @._featured + taiga.defineImmutableProperty @, "searchResult", () => return @._searchResult + taiga.defineImmutableProperty @, "nextSearchPage", () => return @._nextSearchPage + taiga.defineImmutableProperty @, "projectsCount", () => return @._projectsCount + + fetchMostLiked: (params) -> + return @rs.projects.getProjects(params, false) + .then (result) => + data = result.data.slice(0, 5) + + projects = Immutable.fromJS(data) + projects = projects.map(@.decorate) + + @._mostLiked = projects + + fetchMostActive: (params) -> + return @rs.projects.getProjects(params, false) + .then (result) => + data = result.data.slice(0, 5) + + projects = Immutable.fromJS(data) + projects = projects.map(@.decorate) + + @._mostActive = projects + + fetchFeatured: () -> + params = {is_featured: true} + + return @rs.projects.getProjects(params, false) + .then (result) => + data = result.data.slice(0, 4) + + projects = Immutable.fromJS(data) + projects = projects.map(@.decorate) + + @._featured = projects + + resetSearchList: () -> + @._searchResult = Immutable.List() + + fetchStats: () -> + return @rs.stats.discover().then (discover) => + @._projectsCount = discover.getIn(['projects', 'total']) + + fetchSearch: (params) -> + return @rs.projects.getProjects(params) + .then (result) => + @._nextSearchPage = !!result.headers('X-Pagination-Next') + + projects = Immutable.fromJS(result.data) + projects = projects.map(@.decorate) + + @._searchResult = @._searchResult.concat(projects) + +angular.module("taigaDiscover").service("tgDiscoverProjectsService", DiscoverProjectsService) diff --git a/app/modules/discover/services/discover-projects.service.spec.coffee b/app/modules/discover/services/discover-projects.service.spec.coffee new file mode 100644 index 00000000..97adbd0c --- /dev/null +++ b/app/modules/discover/services/discover-projects.service.spec.coffee @@ -0,0 +1,178 @@ +### +# Copyright (C) 2014-2015 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: discover-projects.service.spec.coffee +### + +describe "tgDiscoverProjectsService", -> + discoverProjectsService = provide = null + mocks = {} + + _mockResources = () -> + mocks.resources = { + projects: { + getProjects: sinon.stub() + }, + stats: { + discover: sinon.stub() + } + } + + provide.value "tgResources", mocks.resources + + _mockProjectsService = () -> + mocks.projectsService = { + _decorate: (content) -> + return content.set('decorate', true) + } + + provide.value "tgProjectsService", mocks.projectsService + + _inject = (callback) -> + inject (_tgDiscoverProjectsService_) -> + discoverProjectsService = _tgDiscoverProjectsService_ + callback() if callback + + _mocks = () -> + module ($provide) -> + provide = $provide + _mockResources() + _mockProjectsService() + return null + + _setup = -> + _mocks() + + beforeEach -> + module "taigaDiscover" + _setup() + _inject() + + it "fetch most liked", (done) -> + params = {test: 1} + + mocks.resources.projects.getProjects.withArgs(sinon.match(params), false).promise().resolve({ + data: [ + {id: 1}, + {id: 2}, + {id: 3}, + {id: 4}, + {id: 5}, + {id: 6}, + {id: 7} + ] + }) + + discoverProjectsService.fetchMostLiked(params).then () -> + result = discoverProjectsService._mostLiked.toJS() + + expect(result).to.have.length(5) + expect(result[0].decorate).to.be.ok; + + done() + + it "fetch most active", (done) -> + params = {test: 1} + + mocks.resources.projects.getProjects.withArgs(sinon.match(params), false).promise().resolve({ + data: [ + {id: 1}, + {id: 2}, + {id: 3}, + {id: 4}, + {id: 5}, + {id: 6}, + {id: 7} + ] + }) + + discoverProjectsService.fetchMostActive(params).then () -> + result = discoverProjectsService._mostActive.toJS() + + expect(result).to.have.length(5) + expect(result[0].decorate).to.be.ok; + + done() + + it "fetch featured", (done) -> + mocks.resources.projects.getProjects.withArgs(sinon.match({is_featured: true}), false).promise().resolve({ + data: [ + {id: 1}, + {id: 2}, + {id: 3}, + {id: 4}, + {id: 5}, + {id: 6}, + {id: 7} + ] + }) + + discoverProjectsService.fetchFeatured().then () -> + result = discoverProjectsService._featured.toJS() + + expect(result).to.have.length(4) + expect(result[0].decorate).to.be.ok; + + done() + + it "reset search list", () -> + discoverProjectsService._searchResult = 'xxx' + + discoverProjectsService.resetSearchList() + + expect(discoverProjectsService._searchResult.size).to.be.equal(0) + + it "fetch stats", (done) -> + mocks.resources.stats.discover.promise().resolve(Immutable.fromJS({ + projects: { + total: 3 + } + })) + + discoverProjectsService.fetchStats().then () -> + expect(discoverProjectsService._projectsCount).to.be.equal(3) + + done() + + it "fetch search", (done) -> + params = {test: 1} + + result = { + headers: sinon.stub(), + data: [ + {id: 1}, + {id: 2}, + {id: 3} + ] + } + + result.headers.withArgs('X-Pagination-Next').returns('next') + + mocks.resources.projects.getProjects.withArgs(sinon.match(params)).promise().resolve(result) + + discoverProjectsService._searchResult = Immutable.fromJS([ + {id: 4}, + {id: 5} + ]) + + discoverProjectsService.fetchSearch(params).then () -> + result = discoverProjectsService._searchResult.toJS() + + expect(result).to.have.length(5) + + expect(result[4].decorate).to.be.ok; + + done() diff --git a/app/modules/home/home-controller.spec.coffee b/app/modules/home/home-controller.spec.coffee new file mode 100644 index 00000000..2713181f --- /dev/null +++ b/app/modules/home/home-controller.spec.coffee @@ -0,0 +1,78 @@ +### +# Copyright (C) 2014-2015 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: home.controller.spec.coffee +### + +describe "HomeController", -> + homeCtrl = null + provide = null + controller = null + mocks = {} + + _mockCurrentUserService = () -> + mocks.currentUserService = { + getUser: sinon.stub() + } + + provide.value "tgCurrentUserService", mocks.currentUserService + + _mockLocation = () -> + mocks.location = { + path: sinon.stub() + } + provide.value "$location", mocks.location + + _mockTgNavUrls = () -> + mocks.tgNavUrls = { + resolve: sinon.stub() + } + + provide.value "$tgNavUrls", mocks.tgNavUrls + + _mocks = () -> + module ($provide) -> + provide = $provide + _mockCurrentUserService() + _mockLocation() + _mockTgNavUrls() + + return null + + beforeEach -> + module "taigaHome" + + _mocks() + + inject ($controller) -> + controller = $controller + + it "anonymous home", () -> + homeCtrl = controller "Home", + $scope: {} + + expect(mocks.tgNavUrls.resolve).to.be.calledWith("discover") + expect(mocks.location.path).to.be.calledOnce + + it "non anonymous home", () -> + mocks.currentUserService = { + getUser: Immutable.fromJS({ + id: 1 + }) + } + + expect(mocks.tgNavUrls.resolve).to.be.notCalled + expect(mocks.location.path).to.be.notCalled diff --git a/app/modules/home/home.controller.coffee b/app/modules/home/home.controller.coffee new file mode 100644 index 00000000..a3d9121e --- /dev/null +++ b/app/modules/home/home.controller.coffee @@ -0,0 +1,32 @@ +### +# Copyright (C) 2014-2015 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: home.controller.coffee +### + +class HomeController + @.$inject = [ + "tgCurrentUserService", + "$location", + "$tgNavUrls" + ] + + constructor: (@currentUserService, @location, @navUrls) -> + if not @currentUserService.getUser() + @location.path(@navUrls.resolve("discover")) + + +angular.module("taigaHome").controller("Home", HomeController) diff --git a/app/modules/home/home.scss b/app/modules/home/home.scss index e605acea..9f488e10 100644 --- a/app/modules/home/home.scss +++ b/app/modules/home/home.scss @@ -11,7 +11,7 @@ display: block; } .title-bar { - @extend %title; + @extend %light; @extend %larger; align-content: center; background: $whitish; diff --git a/app/modules/home/projects/home-project-list.jade b/app/modules/home/projects/home-project-list.jade index 0841af36..53336204 100644 --- a/app/modules/home/projects/home-project-list.jade +++ b/app/modules/home/projects/home-project-list.jade @@ -1,13 +1,59 @@ section.home-project-list(ng-if="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") ... + + .home-project(tg-bind-scope, tg-repeat="project in vm.projects") + .tags-container + .project-tag( + style="background: {{tag.get('color')}}" + title="{{tag.get('name')}}" + tg-repeat="tag in project.get('colorized_tags') track by tag.get('name')" + ) + .project-card-inner(href="#", tg-nav="project:project=project.get('slug')") + .project-card-header + a.project-card-logo( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + h2.project-card-name + a( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) {{::project.get('name')}} + span.look-for-people( + ng-if="project.get('is_looking_for_people')" + title="{{ ::project.get('looking_for_people_note') }}" + ) + include ../../../svg/recruit.svg + p.project-card-description {{::project.get('description')| limitTo:100 }} + span(ng-if="::project.get('description').length > 100") ... + .project-card-statistics + span.statistic( + ng-class="{'active': project.get('is_fan')}" + title="{{ 'PROJECT.FANS_COUNTER_TITLE'|translate:{total:project.get('total_fans')||0}:'messageformat' }}" + ) + include ../../../svg/like.svg + span {{::project.get('total_fans')}} + span.statistic( + ng-class="{'active': project.get('is_watcher')}" + title="{{ 'PROJECT.WATCHERS_COUNTER_TITLE'|translate:{total:project.get('total_watchers')||0}:'messageformat' }}" + ) + include ../../../svg/eye.svg + span {{::project.get('total_watchers')}} + span.statistic( + title="{{ 'PROJECT.MEMBERS_COUNTER_TITLE'|translate:{total:project.get('members').size||0}:'messageformat' }}" + ) + include ../../../svg/team.svg + span.statistics-num {{ ::project.get('members').size }} + span.statistic( + ng-if="::project.get('is_private')" + title="{{ 'PROJECT.PRIVATE' | translate }}" + ) + include ../../../svg/lock.svg a.see-more-projects-btn.button-gray( href="#", diff --git a/app/modules/home/projects/home-project-list.scss b/app/modules/home/projects/home-project-list.scss index 21d67b31..c73826b0 100644 --- a/app/modules/home/projects/home-project-list.scss +++ b/app/modules/home/projects/home-project-list.scss @@ -1,52 +1,13 @@ -.home-project-list { - li { - border: 1px solid lighten($gray-light, 15%); - border-radius: 3px; - cursor: pointer; - margin-bottom: .75rem; - padding: 1rem; - text-overflow: ellipsis; - &:hover { - border-color: $primary-light; - transition: all .3s linear; - p { - color: $gray; - transition: color .3s linear; - } - .private path { - fill: $gray; - transition: fill .3s linear; - } - } - a { - display: flex; - flex-direction: column; - min-height: 5rem; - } - } - h2 { - @extend %text; - color: $gray; - font-size: 1.5rem; - line-height: 1.3; - margin-bottom: .5rem; - text-transform: none; - .project-name { - display: inline-block; - max-width: 90%; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: middle; - white-space: nowrap; - } - } - p { - @extend %text; - @extend %xsmall; - color: $gray-light; - line-height: 125%; - margin: 0; - word-wrap: break-word; +@import '../../../styles/dependencies/mixins/project-card'; + +.home-project { + @include project-card; + cursor: pointer; + margin-bottom: 1rem; + transition: .2s; + transition-delay: .1s; + &:hover { + border: 1px solid $primary-light; } } diff --git a/app/modules/home/working-on/working-on.jade b/app/modules/home/working-on/working-on.jade index 13acd194..62e3d755 100644 --- a/app/modules/home/working-on/working-on.jade +++ b/app/modules/home/working-on/working-on.jade @@ -1,4 +1,10 @@ section.working-on-container + + header + h1 + span.green {{"HOME.DASHBOARD" | translate}} + + .title-bar.working-on-title(translate="HOME.WORKING_ON_SECTION") .working-on(ng-if="vm.assignedTo.size") diff --git a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.coffee b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.coffee index aeac042e..ac09365c 100644 --- a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.coffee +++ b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.coffee @@ -27,7 +27,8 @@ DropdownUserDirective = (authService, configService, locationService, scope.vm.logout = -> authService.logout() - locationService.path(navUrlsService.resolve("login")) + locationService.url(navUrlsService.resolve("discover")) + locationService.search({}) scope.vm.sendFeedback = -> feedbackService.sendFeedback() diff --git a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee index d85e69fd..87287d6a 100644 --- a/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee +++ b/app/modules/navigation-bar/dropdown-user/dropdown-user.directive.spec.coffee @@ -50,8 +50,10 @@ describe "dropdownUserDirective", () -> _mockTgLocation = () -> mockTgLocation = { - path: sinon.stub() + url: sinon.stub() + search: sinon.stub() } + provide.value "$tgLocation", mockTgLocation _mockTgNavUrls = () -> @@ -97,16 +99,19 @@ describe "dropdownUserDirective", () -> expect(vm.isFeedbackEnabled).to.be.equal(true) it "dropdown user log out", () -> - mockTgNavUrls.resolve.withArgs("login").returns("/login") + mockTgNavUrls.resolve.withArgs("discover").returns("/discover") elm = createDirective() scope.$apply() vm = elm.isolateScope().vm expect(mockTgAuth.logout.callCount).to.be.equal(0) - expect(mockTgLocation.path.callCount).to.be.equal(0) + expect(mockTgLocation.url.callCount).to.be.equal(0) + expect(mockTgLocation.search.callCount).to.be.equal(0) vm.logout() expect(mockTgAuth.logout.callCount).to.be.equal(1) - expect(mockTgLocation.path.callCount).to.be.equal(1) - expect(mockTgLocation.path.calledWith("/login")).to.be.true + expect(mockTgLocation.url.callCount).to.be.equal(1) + expect(mockTgLocation.search.callCount).to.be.equal(1) + expect(mockTgLocation.url.calledWith("/discover")).to.be.true + expect(mockTgLocation.search.calledWith({})).to.be.true it "dropdown user send feedback", () -> elm = createDirective() diff --git a/app/modules/navigation-bar/navigation-bar.directive.coffee b/app/modules/navigation-bar/navigation-bar.directive.coffee index 67d4589d..c32a792f 100644 --- a/app/modules/navigation-bar/navigation-bar.directive.coffee +++ b/app/modules/navigation-bar/navigation-bar.directive.coffee @@ -17,12 +17,14 @@ # File: navigation-bar.directive.coffee ### -NavigationBarDirective = (currentUserService, navigationBarService, $location) -> +NavigationBarDirective = (currentUserService, navigationBarService, + locationService, navUrlsService) -> + link = (scope, el, attrs, ctrl) -> scope.vm = {} scope.$on "$routeChangeSuccess", () -> - if $location.path() == "/" + if locationService.path() == "/" scope.vm.active = true else scope.vm.active = false @@ -31,6 +33,15 @@ NavigationBarDirective = (currentUserService, navigationBarService, $location) - taiga.defineImmutableProperty(scope.vm, "isAuthenticated", () -> currentUserService.isAuthenticated()) taiga.defineImmutableProperty(scope.vm, "isEnabledHeader", () -> navigationBarService.isEnabledHeader()) + scope.vm.login = -> + nextUrl = encodeURIComponent(locationService.url()) + locationService.url(navUrlsService.resolve("login")) + locationService.search({next: nextUrl}) + + scope.vm.register = -> + nextUrl = encodeURIComponent(locationService.url()) + locationService.url(navUrlsService.resolve("register")) + locationService.search({next: nextUrl}) directive = { templateUrl: "navigation-bar/navigation-bar.html" @@ -42,8 +53,9 @@ NavigationBarDirective = (currentUserService, navigationBarService, $location) - NavigationBarDirective.$inject = [ "tgCurrentUserService", - "tgNavigationBarService" - "$location" + "tgNavigationBarService", + "$tgLocation", + "$tgNavUrls" ] angular.module("taigaNavigationBar").directive("tgNavigationBar", NavigationBarDirective) diff --git a/app/modules/navigation-bar/navigation-bar.directive.spec.coffee b/app/modules/navigation-bar/navigation-bar.directive.spec.coffee index 7c3251bc..36041e2f 100644 --- a/app/modules/navigation-bar/navigation-bar.directive.spec.coffee +++ b/app/modules/navigation-bar/navigation-bar.directive.spec.coffee @@ -41,6 +41,19 @@ describe "navigationBarDirective", () -> provide.value "tgCurrentUserService", mocks.currentUserService + _mocksLocationService = () -> + mocks.locationService = { + url: sinon.stub() + search: sinon.stub() + } + + provide.value "$tgLocation", mocks.locationService + + _mockTgNavUrls = () -> + mocks.navUrls = { + resolve: sinon.stub() + } + provide.value "$tgNavUrls", mocks.navUrls _mockTranslateFilter = () -> mockTranslateFilter = (value) -> @@ -58,6 +71,8 @@ describe "navigationBarDirective", () -> provide = $provide _mocksCurrentUserService() + _mocksLocationService() + _mockTgNavUrls( ) _mockTranslateFilter() _mockTgDropdownProjectListDirective() _mockTgDropdownUserDirective() @@ -90,3 +105,33 @@ describe "navigationBarDirective", () -> mocks.currentUserService.isAuthenticated.returns(true) expect(elm.isolateScope().vm.isAuthenticated).to.be.true + + it "navigation bar login", () -> + mocks.navUrls.resolve.withArgs("login").returns("/login") + nextUrl = "/discover/search?order_by=-total_activity_last_month" + mocks.locationService.url.returns(nextUrl) + elm = createDirective() + scope.$apply() + vm = elm.isolateScope().vm + expect(mocks.locationService.url.callCount).to.be.equal(0) + expect(mocks.locationService.search.callCount).to.be.equal(0) + vm.login() + expect(mocks.locationService.url.callCount).to.be.equal(2) + expect(mocks.locationService.search.callCount).to.be.equal(1) + expect(mocks.locationService.url.calledWith("/login")).to.be.true + expect(mocks.locationService.search.calledWith({next: encodeURIComponent(nextUrl)})).to.be.true + + it "navigation bar register", () -> + mocks.navUrls.resolve.withArgs("register").returns("/register") + nextUrl = "/discover/search?order_by=-total_activity_last_month" + mocks.locationService.url.returns(nextUrl) + elm = createDirective() + scope.$apply() + vm = elm.isolateScope().vm + expect(mocks.locationService.url.callCount).to.be.equal(0) + expect(mocks.locationService.search.callCount).to.be.equal(0) + vm.register() + expect(mocks.locationService.url.callCount).to.be.equal(2) + expect(mocks.locationService.search.callCount).to.be.equal(1) + expect(mocks.locationService.url.calledWith("/register")).to.be.true + expect(mocks.locationService.search.calledWith({next: encodeURIComponent(nextUrl)})).to.be.true diff --git a/app/modules/navigation-bar/navigation-bar.jade b/app/modules/navigation-bar/navigation-bar.jade index 54aa167e..83f49a36 100644 --- a/app/modules/navigation-bar/navigation-bar.jade +++ b/app/modules/navigation-bar/navigation-bar.jade @@ -3,7 +3,7 @@ nav.navbar(ng-if="vm.isEnabledHeader") a.logo( href="#", tg-nav="home", - title="{{'PROJECT.NAVIGATION.DASHBOARD_TITLE' | translate}}") + title="{{'PROJECT.NAVIGATION.HOMEPAGE' | translate}}") include ../../svg/logo.svg @@ -15,16 +15,16 @@ nav.navbar(ng-if="vm.isEnabledHeader") div.nav-right(ng-if="!vm.isAuthenticated") a.login( - tg-nav="login", + ng-click="vm.login()" href="#", - title="{{ 'LOGIN_COMMON.ACTION_SIGN_IN' | translate }}" + title="{{ 'LOGIN_COMMON.ACTION_SIGN_IN' | translate }}" ) {{ 'LOGIN_COMMON.ACTION_SIGN_IN' | translate }} a.register( - tg-nav="register", + ng-click="vm.register()" href="#", - title="{{ 'REGISTER_FORM.ACTION_SIGN_UP' | translate }}" + title="{{ 'REGISTER_FORM.ACTION_SIGN_UP' | translate }}" ) {{ 'REGISTER_FORM.ACTION_SIGN_UP' | translate }} - + div.nav-right(ng-if="vm.isAuthenticated") a(tg-nav="home", ng-class="{active: vm.active}", @@ -32,6 +32,13 @@ nav.navbar(ng-if="vm.isEnabledHeader") include ../../svg/dashboard.svg + a( + href="#", + tg-nav="discover", + title="{{'PROJECT.NAVIGATION.DISCOVER_TITLE' | translate}}", + ) + include ../../svg/discover.svg + div.topnav-dropdown-wrapper(ng-show="vm.projects.size", tg-dropdown-project-list) - //div.topnav-dropdown-wrapper(tg-dropdown-organization-list) + //- div.topnav-dropdown-wrapper(tg-dropdown-organization-list) div.topnav-dropdown-wrapper(tg-dropdown-user) diff --git a/app/modules/navigation-bar/navigation-bar.scss b/app/modules/navigation-bar/navigation-bar.scss index 469ecb8d..440784c0 100644 --- a/app/modules/navigation-bar/navigation-bar.scss +++ b/app/modules/navigation-bar/navigation-bar.scss @@ -32,7 +32,9 @@ $dropdown-width: 350px; } svg { height: 1.6rem; + max-height: 1.6rem; max-width: 2rem; + width: 1.6rem; } path { fill: $white; @@ -79,7 +81,9 @@ $dropdown-width: 350px; } svg { height: 1.2rem; + max-height: 1.2rem; max-width: 1.2rem; + width: 1.2rem; path { fill: $top-icon-color; transition: all .2s; diff --git a/app/modules/profile/profile-favs/items/project.jade b/app/modules/profile/profile-favs/items/project.jade index 2cb121d9..827eff4f 100644 --- a/app/modules/profile/profile-favs/items/project.jade +++ b/app/modules/profile/profile-favs/items/project.jade @@ -1,13 +1,26 @@ .list-itemtype-project - .list-itemtype-project-data - h2 - a( + .list-itemtype-project-left + .list-itemtype-project-data-wrapper + + a.list-itemtype-project-image( href="#" tg-nav="project:project=vm.item.get('slug')" title="{{ ::vm.item.get('name') }}" - ) {{ ::vm.item.get('name') }} - span.private(ng-if="::project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}") - p {{ ::vm.item.get('description') }} + ) + img( + tg-project-logo-src="vm.item" + title="{{ ::vm.item.get('name') }}" + ) + + .list-itemtype-project-data + h2 + a( + href="#" + tg-nav="project:project=vm.item.get('slug')" + title="{{ ::vm.item.get('name') }}" + ) {{ ::vm.item.get('name') }} + span.private(ng-if="::project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}") + p {{ ::vm.item.get('description') }} .list-itemtype-project-tags.tags-container(ng-if="::vm.item.get('tags_colors').size") span.tag( diff --git a/app/modules/profile/profile-projects/profile-projects.jade b/app/modules/profile/profile-projects/profile-projects.jade index 3d4e8b2c..28c2ef45 100644 --- a/app/modules/profile/profile-projects/profile-projects.jade +++ b/app/modules/profile/profile-projects/profile-projects.jade @@ -13,15 +13,24 @@ section.profile-projects .list-itemtype-project(tg-repeat="project in vm.projects") .list-itemtype-project-left - - .project-list-single-title - h2 - a( - href="#" - tg-nav="project:project=project.get('slug')" - title="{{ ::project.get('name') }}" - ) {{::project.get('name')}} - p {{ ::project.get('description') | limitTo:300 }} + .project-list-single-title-wrapper + a.list-itemtype-project-image( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{ ::project.get('name') }}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + .project-list-single-title + h2 + a( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{ ::project.get('name') }}" + ) {{::project.get('name')}} + p {{ ::project.get('description') | limitTo:300 }} .list-itemtype-project-tags.tags-container(ng-if="::project.get('tags').size") span.tag( diff --git a/app/modules/profile/styles/profile-content-tabs.scss b/app/modules/profile/styles/profile-content-tabs.scss index 492778cc..6b5340be 100644 --- a/app/modules/profile/styles/profile-content-tabs.scss +++ b/app/modules/profile/styles/profile-content-tabs.scss @@ -4,7 +4,7 @@ .tab { color: $gray-light; display: inline-block; - padding: 1rem 1.25rem; + padding: 1rem; &:hover, &.active { color: $grayer; diff --git a/app/modules/projects/listing/projects-listing.jade b/app/modules/projects/listing/projects-listing.jade index 60dab648..3e1e0de9 100644 --- a/app/modules/projects/listing/projects-listing.jade +++ b/app/modules/projects/listing/projects-listing.jade @@ -1,28 +1,60 @@ -div.project-list-wrapper.centered - div.project-list-title +.project-list-wrapper.centered + .project-list-title h1(translate="PROJECTS.MY_PROJECTS") - div.create-options - a.create-project-btn.button-green(href="#", ng-click="vm.newProject()", title="{{'PROJECT.NAVIGATION.ACTION_CREATE_PROJECT' | translate}}", translate="PROJECT.NAVIGATION.ACTION_CREATE_PROJECT") + .create-options + a.create-project-btn.button-green( + href="#" + ng-click="vm.newProject()" + title="{{'PROJECT.NAVIGATION.ACTION_CREATE_PROJECT' | translate}}" + translate="PROJECT.NAVIGATION.ACTION_CREATE_PROJECT" + ) span(tg-import-project-button) - a.button-blackish.import-project-button(href="", title="{{'PROJECT.NAVIGATION.TITLE_IMPORT_PROJECT' | translate}}") + a.button-blackish.import-project-button( + href="" + title="{{'PROJECT.NAVIGATION.TITLE_IMPORT_PROJECT' | translate}}" + ) span.icon.icon-upload input.import-file.hidden(type="file") section.project-list-section - div.project-list + .project-list ul(tg-sort-projects="vm.projects") - li.list-itemtype-project(tg-bind-scope, tg-repeat="project in vm.projects track by project.get('id')") - div.list-itemtype-project-left - div.list-itemtype-project-data - h2 - a(href="#", tg-nav="project:project=project.get('slug')", 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:300 }} - span(ng-if="::project.get('description').length > 300") ... - - div.list-itemtype-project-tags.tag-container(ng-if="::project.get('tags').size") - span.tag(style='border-left: 5px solid {{::tag.get("color")}};', tg-repeat="tag in ::project.get('colorized_tags')") + li.list-itemtype-project( + tg-bind-scope + tg-repeat="project in vm.projects track by project.get('id')" + ) + .list-itemtype-project-left + + .list-itemtype-project-data-wrapper + a.list-itemtype-project-image( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{ ::project.get('name') }}" + ) + img( + tg-project-logo-src="::project" + alt="{{::project.get('name')}}" + ) + .list-itemtype-project-data + h2 + a( + href="#" + tg-nav="project:project=project.get('slug')" + 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:300 }} + span(ng-if="::project.get('description').length > 300") ... + + .list-itemtype-project-tags.tag-container(ng-if="::project.get('tags').size") + span.tag( + style='border-left: 5px solid {{::tag.get("color")}};' + tg-repeat="tag in ::project.get('colorized_tags')" + ) span.tag-name {{::tag.get('name')}} span.drag.icon.icon-drag-v diff --git a/app/modules/projects/listing/styles/profile-projects.scss b/app/modules/projects/listing/styles/profile-projects.scss index 534e9738..e068e7c7 100644 --- a/app/modules/projects/listing/styles/profile-projects.scss +++ b/app/modules/projects/listing/styles/profile-projects.scss @@ -4,6 +4,12 @@ display: flex; justify-content: space-between; min-height: 10rem; + .project-list-single-title-wrapper { + display: flex; + } + .list-itemtype-project-image { + flex-shrink: 0; + } .list-itemtype-project-right { display: flex; flex-direction: column; diff --git a/app/modules/projects/listing/styles/project-list.scss b/app/modules/projects/listing/styles/project-list.scss index daf97513..b9f68b93 100644 --- a/app/modules/projects/listing/styles/project-list.scss +++ b/app/modules/projects/listing/styles/project-list.scss @@ -54,6 +54,13 @@ opacity: 1; } } + .list-itemtype-project-data-wrapper { + display: flex; + } + .list-itemtype-project-image { + flex-shrink: 0; + margin-right: 1rem; + } } .drag { @extend %large; diff --git a/app/modules/projects/project/project.jade b/app/modules/projects/project/project.jade index 8977c920..b646fbff 100644 --- a/app/modules/projects/project/project.jade +++ b/app/modules/projects/project/project.jade @@ -2,51 +2,67 @@ div.wrapper tg-project-menu div.single-project.centered section.single-project-intro - div.intro-options - h1 - span.project-name {{::vm.project.get("name")}} - span.private( - ng-if="::vm.project.get('is_private')" - title="{{'PROJECT.PRIVATE' | translate}}" - ) - include ../../../svg/lock.svg - - //- Like and wacht buttons for authenticated users - div.track-buttons-container(ng-if="vm.user") - tg-like-project-button(project="vm.project") - tg-watch-project-button(project="vm.project") - - //- Like and wacht buttons for anonymous users - div.track-container(ng-if="!vm.user") - .list-itemtype-track - span.list-itemtype-track-likers( - title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_fans\")||0}:'messageformat' }}" - ) - span.icon - include ../../../svg/like.svg - span {{ ::vm.project.get('total_fans') }} - - span.list-itemtype-track-watchers( - title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_watchers\")||0}:'messageformat' }}" - ) - span.icon - include ../../../svg/watch.svg - span {{ ::vm.project.get('total_watchers') }} - - p.description {{vm.project.get('description')}} - - div.single-project-tags.tags-container(ng-if="::vm.project.get('tags').size") - span.tag( - style='border-left: 5px solid {{::tag.get("color")}};', - tg-repeat="tag in ::vm.project.get('colorized_tags')" + .project-logo( + href="#" + tg-nav="project:project=project.get('slug')" + title="{{::project.get('name')}}" + ) + img( + tg-project-logo-src="vm.project" + alt="{{::vm.project.get('name')}}" ) - span.tag-name {{::tag.get('name')}} + .single-project-title-wrapper + .intro-options + .intro-title + h1 + span.project-name {{::vm.project.get("name")}} + span.private( + ng-if="::vm.project.get('is_private')" + title="{{'PROJECT.PRIVATE' | translate}}" + ) + include ../../../svg/lock.svg + + div.track-buttons-container(ng-if="vm.user") + tg-like-project-button(project="vm.project") + tg-watch-project-button(project="vm.project") + + div.track-container(ng-if="!vm.user") + .list-itemtype-track + span.list-itemtype-track-likers( + title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_fans\")||0}:'messageformat' }}" + ) + span.icon + include ../../../svg/like.svg + span {{ ::vm.project.get('total_fans') }} + + span.list-itemtype-track-watchers( + title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_watchers\")||0}:'messageformat' }}" + ) + span.icon + include ../../../svg/watch.svg + span {{ ::vm.project.get('total_watchers') }} + + p.description {{vm.project.get('description')}} + + div.single-project-tags.tags-container(ng-if="::vm.project.get('tags').size") + span.tag( + style='border-left: 5px solid {{::tag.get("color")}};', + tg-repeat="tag in ::vm.project.get('colorized_tags')" + ) + span.tag-name {{::tag.get('name')}} div.project-data section.timeline(ng-if="vm.project") div(tg-user-timeline, projectId="vm.project.get('id')") section.involved-data + .looking-for-people(ng-if="vm.project.get('is_looking_for_people')") + img( + src="/#{v}/images/looking-for-people.png" + title="{{'PROJECT.LOOKING_FOR_PEOPLE' | translate}}" + ) + h3 {{'PROJECT.LOOKING_FOR_PEOPLE' | translate}} + p(ng-if="vm.project.get('looking_for_people_note')") {{::vm.project.get('looking_for_people_note')}}" h2.title {{"PROJECT.SECTION.TEAM" | translate}} ul.involved-team li(tg-repeat="member in vm.members") @@ -55,11 +71,3 @@ div.wrapper title="{{::member.get('full_name')}}" ) img(ng-src="{{::member.get('photo')}}", alt="{{::member.get('full_name')}}") - //- - h2.title Organizations - div.involved-organization - a(href="", title="User Name") - img( - src="https://s3.amazonaws.com/uifaces/faces/twitter/dan_higham/48.jpg" - alt="{{member.full_name}}" - ) diff --git a/app/coffee/modules/resources/attachments.coffee b/app/modules/resources/attachments-resource.service.coffee similarity index 59% rename from app/coffee/modules/resources/attachments.coffee rename to app/modules/resources/attachments-resource.service.coffee index 1ed950cb..94cb1bb2 100644 --- a/app/coffee/modules/resources/attachments.coffee +++ b/app/modules/resources/attachments-resource.service.coffee @@ -1,7 +1,11 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian +# Copyright (C) 2014-2016 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 @@ -16,29 +20,57 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # -# File: modules/resources/attachments.coffee +# File: attachments-resource.service.coffee ### - taiga = @.taiga sizeFormat = @.taiga.sizeFormat - -resourceProvider = ($rootScope, $config, $urls, $model, $repo, $auth, $q) -> +Resource = (urlsService, http, config, $rootScope, $q, storage) -> service = {} - service.list = (urlName, objectId, projectId) -> - params = {object_id: objectId, project: projectId} - return $repo.queryMany(urlName, params) + service.list = (type, objectId, projectId) -> + urlname = "attachments/#{type}" + + params = {object_id: objectId, project: projectId} + httpOptions = { + headers: { + "x-disable-pagination": "1" + } + } + + url = urlsService.resolve(urlname) + + return http.get(url, params, httpOptions) + .then (result) -> Immutable.fromJS(result.data) + + service.delete = (type, id) -> + urlname = "attachments/#{type}" + + url = urlsService.resolve(urlname) + "/#{id}" + + return http.delete(url) + + service.patch = (type, id, patch) -> + urlname = "attachments/#{type}" + + url = urlsService.resolve(urlname) + "/#{id}" + + return http.patch(url, patch) + + service.create = (type, projectId, objectId, file) -> + urlname = "attachments/#{type}" + + url = urlsService.resolve(urlname) - service.create = (urlName, projectId, objectId, file) -> defered = $q.defer() if file is undefined defered.reject(null) return defered.promise - maxFileSize = $config.get("maxUploadFileSize", null) + maxFileSize = config.get("maxUploadFileSize", null) + if maxFileSize and file.size > maxFileSize response = { status: 413, @@ -61,13 +93,13 @@ resourceProvider = ($rootScope, $config, $urls, $model, $repo, $auth, $q) -> status = evt.target.status try - data = JSON.parse(evt.target.responseText) + attachment = JSON.parse(evt.target.responseText) catch - data = {} + attachment = {} if status >= 200 and status < 400 - model = $model.make_model(urlName, data) - defered.resolve(model) + attachment = Immutable.fromJS(attachment) + defered.resolve(attachment) else response = { status: status, @@ -90,17 +122,26 @@ resourceProvider = ($rootScope, $config, $urls, $model, $repo, $auth, $q) -> xhr.addEventListener("load", uploadComplete, false) xhr.addEventListener("error", uploadFailed, false) - xhr.open("POST", $urls.resolve(urlName)) - xhr.setRequestHeader("Authorization", "Bearer #{$auth.getToken()}") + token = storage.get('token') + + xhr.open("POST", url) + xhr.setRequestHeader("Authorization", "Bearer #{token}") xhr.setRequestHeader('Accept', 'application/json') xhr.send(data) return defered.promise - return (instance) -> - instance.attachments = service + return () -> + return {"attachments": service} +Resource.$inject = [ + "$tgUrls", + "$tgHttp", + "$tgConfig", + "$rootScope", + "$q", + "$tgStorage" +] -module = angular.module("taigaResources") -module.factory("$tgAttachmentsResourcesProvider", ["$rootScope", "$tgConfig", "$tgUrls", "$tgModel", "$tgRepo", - "$tgAuth", "$q", resourceProvider]) +module = angular.module("taigaResources2") +module.factory("tgAttachmentsResource", Resource) diff --git a/app/modules/resources/projects-resource.service.coffee b/app/modules/resources/projects-resource.service.coffee index 35de7d51..4385b90c 100644 --- a/app/modules/resources/projects-resource.service.coffee +++ b/app/modules/resources/projects-resource.service.coffee @@ -22,6 +22,20 @@ pagination = () -> Resource = (urlsService, http, paginateResponseService) -> service = {} + service.getProjects = (params = {}, pagination = true) -> + url = urlsService.resolve("projects") + + httpOptions = {} + + if !pagination + httpOptions = { + headers: { + "x-disable-pagination": "1" + } + } + + return http.get(url, params, httpOptions) + service.getProjectBySlug = (projectSlug) -> url = urlsService.resolve("projects") diff --git a/app/modules/resources/resources.coffee b/app/modules/resources/resources.coffee index 5669d85e..e8a4c33a 100644 --- a/app/modules/resources/resources.coffee +++ b/app/modules/resources/resources.coffee @@ -24,7 +24,9 @@ services = [ "tgUserstoriesResource", "tgTasksResource", "tgIssuesResource", - "tgExternalAppsResource" + "tgExternalAppsResource", + "tgAttachmentsResource", + "tgStatsResource" ] Resources = ($injector) -> diff --git a/app/modules/resources/stats-resource.service.coffee b/app/modules/resources/stats-resource.service.coffee new file mode 100644 index 00000000..2376ae4c --- /dev/null +++ b/app/modules/resources/stats-resource.service.coffee @@ -0,0 +1,34 @@ +### +# Copyright (C) 2014-2015 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: stats-resource.service.coffee +### + +Resource = (urlsService, http) -> + service = {} + + service.discover = (applicationId, state) -> + url = urlsService.resolve("stats-discover") + return http.get(url).then (result) -> + Immutable.fromJS(result.data) + + return () -> + return {"stats": service} + +Resource.$inject = ["$tgUrls", "$tgHttp"] + +module = angular.module("taigaResources2") +module.factory("tgStatsResource", Resource) diff --git a/app/modules/services/attachments.service.coffee b/app/modules/services/attachments.service.coffee new file mode 100644 index 00000000..5ebfa0ce --- /dev/null +++ b/app/modules/services/attachments.service.coffee @@ -0,0 +1,87 @@ +### +# Copyright (C) 2014-2015 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: attachments.service.coffee +### + +sizeFormat = @.taiga.sizeFormat + +class AttachmentsService + @.$inject = [ + "$tgConfirm", + "$tgConfig", + "$translate", + "tgResources" + ] + + constructor: (@confirm, @config, @translate, @rs) -> + @.maxFileSize = @.getMaxFileSize() + + if @.maxFileSize + @.maxFileSizeFormated = sizeFormat(@.maxFileSize) + + sizeError: (file) -> + message = @translate.instant("ATTACHMENT.ERROR_MAX_SIZE_EXCEEDED", { + fileName: file.name, + fileSize: sizeFormat(file.size), + maxFileSize: @.maxFileSizeFormated + }) + + @confirm.notify("error", message) + + validate: (file) -> + if @.maxFileSize && file.size > @.maxFileSize + @.sizeError(file) + + return false + + return true + + getMaxFileSize: () -> + return @config.get("maxUploadFileSize", null) + + list: (type, objId, projectId) -> + return @rs.attachments.list(type, objId, projectId).then (attachments) => + return attachments.sortBy (attachment) => attachment.get('order') + + delete: (type, id) -> + return @rs.attachments.delete(type, id) + + saveError: (file, data) -> + message = "" + + if file + message = @translate.instant("ATTACHMENT.ERROR_UPLOAD_ATTACHMENT", { + fileName: file.name, errorMessage: data.data._error_message + }) + + @confirm.notify("error", message) + + upload: (file, objId, projectId, type) -> + promise = @rs.attachments.create(type, projectId, objId, file) + + promise.then null, @.saveError.bind(this, file) + + return promise + + patch: (id, type, patch) -> + promise = @rs.attachments.patch(type, id, patch) + + promise.then null, @.saveError.bind(this, null) + + return promise + +angular.module("taigaCommon").service("tgAttachmentsService", AttachmentsService) diff --git a/app/modules/services/attachments.service.spec.coffee b/app/modules/services/attachments.service.spec.coffee new file mode 100644 index 00000000..d880d12d --- /dev/null +++ b/app/modules/services/attachments.service.spec.coffee @@ -0,0 +1,178 @@ +### +# Copyright (C) 2014-2015 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: attachments.service.spec.coffee +### + +describe "tgAttachmentsService", -> + attachmentsService = provide = null + mocks = {} + + _mockTgConfirm = () -> + mocks.confirm = { + notify: sinon.stub() + } + + provide.value "$tgConfirm", mocks.confirm + + _mockTgConfig = () -> + mocks.config = { + get: sinon.stub() + } + + mocks.config.get.withArgs('maxUploadFileSize', null).returns(3000) + + provide.value "$tgConfig", mocks.config + + _mockRs = () -> + mocks.rs = {} + + provide.value "tgResources", mocks.rs + + _mockTranslate = () -> + mocks.translate = { + instant: sinon.stub() + } + + provide.value "$translate", mocks.translate + + + _inject = (callback) -> + inject (_tgAttachmentsService_) -> + attachmentsService = _tgAttachmentsService_ + callback() if callback + + _mocks = () -> + module ($provide) -> + provide = $provide + _mockTgConfirm() + _mockTgConfig() + _mockRs() + _mockTranslate() + + return null + + _setup = -> + _mocks() + + beforeEach -> + module "taigaCommon" + _setup() + _inject() + + it "maxFileSize formated", () -> + expect(attachmentsService.maxFileSizeFormated).to.be.equal("2.9 KB") + + it "sizeError, send notification", () -> + file = { + name: 'test', + size: 3000 + } + + mocks.translate.instant.withArgs('ATTACHMENT.ERROR_MAX_SIZE_EXCEEDED').returns('message') + + attachmentsService.sizeError(file) + + expect(mocks.confirm.notify).to.have.been.calledWith('error', 'message') + + it "invalid, validate", () -> + file = { + name: 'test', + size: 4000 + } + + result = attachmentsService.validate(file) + + expect(result).to.be.false + + it "valid, validate", () -> + file = { + name: 'test', + size: 1000 + } + + result = attachmentsService.validate(file) + + expect(result).to.be.true + + it "get max file size", () -> + result = attachmentsService.getMaxFileSize() + + expect(result).to.be.equal(3000) + + it "delete", () -> + mocks.rs.attachments = { + delete: sinon.stub() + } + + attachmentsService.delete('us', 2) + + expect(mocks.rs.attachments.delete).to.have.been.calledWith('us', 2) + + it "upload", (done) -> + file = { + id: 1 + } + + objId = 2 + projectId = 2 + type = 'us' + + mocks.rs.attachments = { + create: sinon.stub().promise() + } + + mocks.rs.attachments.create.withArgs('us', type, objId, file).resolve() + + attachmentsService.sizeError = sinon.spy() + + attachmentsService.upload(file, objId, projectId, 'us').then () -> + expect(mocks.rs.attachments.create).to.have.been.calledOnce + done() + + it "patch", (done) -> + file = { + id: 1 + } + + objId = 2 + type = 'us' + + patch = { + id: 2 + } + + mocks.rs.attachments = { + patch: sinon.stub().promise() + } + + mocks.rs.attachments.patch.withArgs('us', objId, patch).resolve() + + attachmentsService.sizeError = sinon.spy() + + attachmentsService.patch(objId, 'us', patch).then () -> + expect(mocks.rs.attachments.patch).to.have.been.calledOnce + done() + + it "error", () -> + mocks.translate.instant.withArgs("ATTACHMENT.ERROR_MAX_SIZE_EXCEEDED").returns("msg") + + attachmentsService.sizeError({ + name: 'name', + size: 123 + }) + + expect(mocks.confirm.notify).to.have.been.calledWith('error', 'msg') diff --git a/app/modules/services/lightbox-factory.service.coffee b/app/modules/services/lightbox-factory.service.coffee index 33b92464..04c9822f 100644 --- a/app/modules/services/lightbox-factory.service.coffee +++ b/app/modules/services/lightbox-factory.service.coffee @@ -21,9 +21,11 @@ class LightboxFactory @.$inject = ["$rootScope", "$compile"] constructor: (@rootScope, @compile) -> - create: (name, attrs) -> + create: (name, attrs, scopeAttrs) -> scope = @rootScope.$new() + scope = _.merge(scope, scopeAttrs) + elm = $("
") .attr(name, true) .attr("tg-bind-scope", true) diff --git a/app/modules/services/project.service.coffee b/app/modules/services/project.service.coffee index 1f576dc5..ca118352 100644 --- a/app/modules/services/project.service.coffee +++ b/app/modules/services/project.service.coffee @@ -67,6 +67,9 @@ class ProjectService @._section = null @._sectionsBreadcrumb = Immutable.List() + hasPermission: (permission) -> + return @._project.get('my_permissions').indexOf(permission) != -1 + fetchProject: () -> pslug = @.project.get('slug') diff --git a/app/modules/services/project.service.spec.coffee b/app/modules/services/project.service.spec.coffee index 18f91fa7..8b4a11f9 100644 --- a/app/modules/services/project.service.spec.coffee +++ b/app/modules/services/project.service.spec.coffee @@ -140,3 +140,20 @@ describe "tgProjectService", -> expect(projectService.activeMembers.size).to.be.equal(0); expect(projectService.section).to.be.null; expect(projectService.sectionsBreadcrumb.size).to.be.equal(0); + + it "has permissions", () -> + project = Immutable.Map({ + id: 1, + my_permissions: [ + 'test1', + 'test2' + ] + }) + + projectService._project = project + + perm1 = projectService.hasPermission('test2') + perm2 = projectService.hasPermission('test3') + + expect(perm1).to.be.true + expect(perm2).to.be.false diff --git a/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee b/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee index d221977b..5306ac5f 100644 --- a/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee +++ b/app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee @@ -122,7 +122,8 @@ timelineType = (timeline, event) -> check: (timeline, event) -> if timeline.hasIn(['data', 'value_diff']) && timeline.getIn(['data', 'value_diff', 'key']) == 'moveInBacklog' && - event.type == 'change' + event.type == 'change' && + event.obj == 'userstory' return timeline.getIn(['data', 'value_diff', 'value', 'milestone']).get(1) == null @@ -134,7 +135,9 @@ timelineType = (timeline, event) -> check: (timeline, event) -> return timeline.hasIn(['data', 'value_diff']) && timeline.getIn(['data', 'value_diff', 'key']) == 'moveInBacklog' && - event.type == 'change' + event.type == 'change' && + event.obj == 'userstory' + key: 'TIMELINE.US_ADDED_MILESTONE', translate_params: ['username', 'obj_name', 'sprint_name'] }, diff --git a/app/modules/user-timeline/user-timeline/user-timeline.controller.coffee b/app/modules/user-timeline/user-timeline/user-timeline.controller.coffee index 4641a76d..b426672c 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.controller.coffee +++ b/app/modules/user-timeline/user-timeline/user-timeline.controller.coffee @@ -1,7 +1,10 @@ ### -# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Andrey Antukh # Copyright (C) 2014-2016 Jesús Espino Garcia # Copyright (C) 2014-2016 David Barragán Merino +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Juan Francisco Alcántara +# Copyright (C) 2014-2016 Xavi Julian # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/app/modules/user-timeline/user-timeline/user-timeline.service.coffee b/app/modules/user-timeline/user-timeline/user-timeline.service.coffee index 42a72a06..61adc2a4 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.service.coffee +++ b/app/modules/user-timeline/user-timeline/user-timeline.service.coffee @@ -153,7 +153,11 @@ class UserTimelineService extends taiga.Service values_diff = Immutable.Map({'blocked': values_diff}) if values_diff.has('milestone') - values_diff = Immutable.Map({'moveInBacklog': values_diff}) + if event.obj == 'userstory' + values_diff = Immutable.Map({'moveInBacklog': values_diff}) + else + values_diff = values_diff.deleteIn(['values_diff', 'milestone']) + else if event.obj == 'milestone' values_diff = Immutable.Map({'milestone': values_diff}) diff --git a/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee b/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee index 7e2b131d..60912d62 100644 --- a/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee +++ b/app/modules/user-timeline/user-timeline/user-timeline.service.spec.coffee @@ -201,7 +201,7 @@ describe "tgUserTimelineService", -> expect(res.get('data').size).to.be.equal(13) items = config.filter(res.get('data')) - expect(items.size).to.be.equal(6) + expect(items.size).to.be.equal(5) return true @@ -224,7 +224,7 @@ describe "tgUserTimelineService", -> expect(res.get('data').size).to.be.equal(13) items = config.filter(res.get('data')) - expect(items.size).to.be.equal(6) + expect(items.size).to.be.equal(5) return true @@ -247,7 +247,7 @@ describe "tgUserTimelineService", -> expect(res.get('data').size).to.be.equal(13) items = config.filter(res.get('data')) - expect(items.size).to.be.equal(6) + expect(items.size).to.be.equal(5) return true diff --git a/app/partials/admin/admin-project-profile.jade b/app/partials/admin/admin-project-profile.jade index aebc5d45..235202c9 100644 --- a/app/partials/admin/admin-project-profile.jade +++ b/app/partials/admin/admin-project-profile.jade @@ -1,7 +1,10 @@ doctype html -div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl", - ng-init="section='admin'; sectionName='ADMIN.PROJECT_PROFILE.PROJECT_DETAILS'") +div.wrapper( + tg-project-profile + ng-controller="ProjectProfileController as ctrl" + ng-init="section='admin'; sectionName='ADMIN.PROJECT_PROFILE.PROJECT_DETAILS'" +) tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-profile") @@ -15,44 +18,137 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl" include ../includes/components/mainTitle form - fieldset - label(for="project-name", translate="ADMIN.PROJECT_PROFILE.PROJECT_NAME") - input(type="text", name="name", placeholder="{{'ADMIN.PROJECT_PROFILE.PROJECT_NAME' | translate}}", id="project-name", - ng-model="project.name", data-required="true", maxlength="45") - fieldset - label(for="project-sprints", translate="ADMIN.PROJECT_PROFILE.NUMBER_SPRINTS") - input(type="number", name="total_milestones", min="0", placeholder="{{'ADMIN.PROJECT_PROFILE.NUMBER_SPRINTS' | translate}}", - id="project-sprints", ng-model="project.total_milestones", data-type="digits") + .project-details-image(tg-project-logo) + fieldset.image-container + img.image( + tg-project-logo-src="project._attrs" + alt="logo" + ) + .loading-overlay + img.loading-spinner( + src="/#{v}/svg/spinner-circle.svg", + alt="{{'COMMON.LOADING' | translate}}" + ) + input.hidden( + type="file" + id="logo-field" + tg-project-logo-model="logoAttachment" + ) + p.image-help + span {{ 'ADMIN.PROJECT_PROFILE.LOGO_HELP' | translate }} + span.size-info.hidden(tg-bo-html="maxFileSizeMsg") - fieldset - 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") + a.button-green.change.js-change-logo( + href="#" + title="{{'ADMIN.PROJECT_PROFILE.CHANGE_LOGO' | translate}}" + ) {{'ADMIN.PROJECT_PROFILE.CHANGE_LOGO' | translate}} - fieldset - label(for="tags", translate="ADMIN.PROJECT_PROFILE.TAGS") - div.tags-block(ng-if="project.id", tg-lb-tag-line, ng-model="project.tags") + a.use-default-image.js-use-default-logo( + href="#" + title="{{ 'ADMIN.PROJECT_PROFILE.ACTION_USE_DEFAULT_LOGO' | translate }}" + ) {{ 'ADMIN.PROJECT_PROFILE.ACTION_USE_DEFAULT_LOGO' | translate }} - fieldset - label(for="project-description", translate="ADMIN.PROJECT_PROFILE.DESCRIPTION") - textarea(name="description", ng-attr-placeholder="{{'ADMIN.PROJECT_PROFILE.DESCRIPTION' | translate}}", id="project-description", - ng-model="project.description", data-required="true") - div - div.privacy-settings - div - input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="false") - label.trans-button(for="public-project") - span(translate="ADMIN.PROJECT_PROFILE.PUBLIC_PROJECT") - div - input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="true") - label.trans-button(for="private-project") - span(translate="ADMIN.PROJECT_PROFILE.PRIVATE_PROJECT") + .project-details-form-data - button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.SAVE") - a.delete-project(href="", title="{{'ADMIN.PROJECT_PROFILE.DELETE' | translate}}", ng-click="ctrl.openDeleteLightbox()", translate="ADMIN.PROJECT_PROFILE.DELETE") + fieldset + label(for="project-name") {{ 'ADMIN.PROJECT_PROFILE.PROJECT_NAME' | translate }} + input( + type="text" + name="name" + placeholder="{{'ADMIN.PROJECT_PROFILE.PROJECT_NAME' | translate}}" + id="project-name" + ng-model="project.name" + data-required="true" + maxlength="45" + ) + + fieldset + label(for="project-description") {{ 'ADMIN.PROJECT_PROFILE.DESCRIPTION' | translate }} + textarea( + name="description" + ng-attr-placeholder="{{'ADMIN.PROJECT_PROFILE.DESCRIPTION' | translate}}" + id="project-description" + ng-model="project.description" + data-required="true" + ) + + fieldset + label(for="tags") {{ 'ADMIN.PROJECT_PROFILE.TAGS' | translate }} + div.tags-block( + ng-if="project.id" + tg-lb-tag-line + ng-model="project.tags" + ) + fieldset.looking-for-people + .looking-for-people-selector + span {{ 'ADMIN.PROJECT_PROFILE.RECRUITING' | translate }} + span( + title="{{ 'ADMIN.PROJECT_PROFILE.RECRUITING_MESSAGE' | translate }}" + ) + include ../../svg/recruit.svg + div.check + input( + type="checkbox", + ng-model="project.is_looking_for_people" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") + + .looking-for-people-reason(ng-show="project.is_looking_for_people") + label {{ 'ADMIN.PROJECT_PROFILE.RECRUITING_MESSAGE' | translate }} + input( + type="text" + maxlength="200" + ng-model="project.looking_for_people_note" + placeholder="{{ 'ADMIN.PROJECT_PROFILE.RECRUITING_PLACEHOLDER' | translate }}" + ) + + fieldset + .project-privacy-settings + div.privacy-option + input.privacy-project( + type="radio" + id="private-project" + name="privacy-project" + ng-model="project.is_private" + ng-value="false" + ) + label.trans-button(for="private-project") {{ 'ADMIN.PROJECT_PROFILE.PUBLIC_PROJECT' | translate }} + span(title="{{ 'ADMIN.PROJECT_PROFILE.PUBLIC_PROJECT_DESC' | translate }}") + include ../../svg/help.svg + + div.privacy-option + input.privacy-project( + type="radio" + id="public-project" + name="privacy-project" + ng-model="project.is_private" + ng-value="true" + ) + label.trans-button(for="public-project") {{'ADMIN.PROJECT_PROFILE.PRIVATE_PROJECT' | translate }} + span(title="{{ 'ADMIN.PROJECT_PROFILE.PRIVATE_PROJECT_DESC' | translate }}") + include ../../svg/help.svg + + a.private-or-public( + href="https://taiga.io/support/whats-the-difference-between-public-and-private-projects/" + target="_blank" + ) + span(title="{{ 'ADMIN.PROJECT_PROFILE.PRIVATE_OR_PUBLIC' | translate }}") + include ../../svg/help.svg + span {{'ADMIN.PROJECT_PROFILE.PRIVATE_OR_PUBLIC' | translate }} + button.button-green.submit-button( + type="submit" + title="{{'COMMON.SAVE' | translate}}" + translate="COMMON.SAVE" + ) + a.delete-project( + href="" + title="{{'ADMIN.PROJECT_PROFILE.DELETE' | translate}}" + ng-click="ctrl.openDeleteLightbox()" + ) {{ 'ADMIN.PROJECT_PROFILE.DELETE' | translate }} div.lightbox.lightbox-delete-project(tg-lb-delete-project) include ../includes/modules/lightbox-delete-project diff --git a/app/partials/attachment/attachment-edit.jade b/app/partials/attachment/attachment-edit.jade deleted file mode 100644 index 86a57f05..00000000 --- a/app/partials/attachment/attachment-edit.jade +++ /dev/null @@ -1,19 +0,0 @@ -.attachment-name - span.icon.icon-document - a(href!="<%- url %>", title!="<%- title %>", target="_blank", download!="<%- name %>") - | <%- name %> -.attachment-size - span <%- size %> - -.editable.editable-attachment-comment - input(type="text", name="description", maxlength="140", - value!="<%- description %>", placeholder="{{'ATTACHMENT.DESCRIPTION' | translate}}") - -.editable.editable-attachment-deprecated - input(type="checkbox", name="is-deprecated", - id!="attach-<%- id %>-is-deprecated") - label(for!="attach-<%- id %>-is-deprecated", translate="{{'ATTACHMENT.DEPRECATED_FILE' | translate}}") - -.attachment-settings - a.editable-settings.icon.icon-floppy(href="", title="{{'COMMON.SAVE' | translate}}") - a.editable-settings.icon.icon-delete(href="", title="{{'COMMON.CANCEL' | translate}}") diff --git a/app/partials/attachment/attachment.jade b/app/partials/attachment/attachment.jade deleted file mode 100644 index 9e2c76da..00000000 --- a/app/partials/attachment/attachment.jade +++ /dev/null @@ -1,19 +0,0 @@ -.attachment-name - a(href!="<%- url %>", title!="<%- title %>", target="_blank", download!="<%- name %>") - span.icon.icon-documents - span <%- name %> -.attachment-size - span <%- size %> - -.attachment-comments - <% if (isDeprecated){ %> - span.deprecated-file {{'ATTACHMENT.DEPRECATED' | translate}} - <% } %> - span <%- description %> - -<% if (modifyPermission) {%> -.attachment-settings - a.settings.icon.icon-edit(href="", title="{{'COMMON.EDIT' | translate}}") - a.settings.icon.icon-delete(href="", title="{{'COMMON.DELETE' | translate}}") - a.settings.icon.icon-drag-v(href="", title="{{'COMMON.DRAG' | translate}}") -<% } %> diff --git a/app/partials/attachment/attachments.jade b/app/partials/attachment/attachments.jade deleted file mode 100644 index 25ada019..00000000 --- a/app/partials/attachment/attachments.jade +++ /dev/null @@ -1,30 +0,0 @@ -section.attachments - .attachments-header - h3.attachments-title - span.attachments-num(tg-bind-html="ctrl.attachmentsCount") - span.attachments-text(translate="ATTACHMENT.SECTION_NAME") - .add-attach(tg-check-permission!="modify_<%- type %>", title!="{{'ATTACHMENT.ADD' | translate}}") - <% if (maxFileSize){ %> - span.size-info.hidden(translate="ATTACHMENT.MAX_FILE_SIZE", translate-values!="{ 'maxFileSize': '<%- maxFileSize %>'}") - <% }; %> - label(for="add-attach", class="icon icon-plus related-tasks-buttons") - input(id="add-attach", type="file", multiple="multiple") - - .attachment-body.sortable - .single-attachment(ng-repeat="attach in ctrl.attachments|filter:ctrl.filterAttachments track by attach.id" tg-attachment="attach", tg-bind-scope) - - .single-attachment(ng-repeat="file in ctrl.uploadingAttachments") - .attachment-name - a(href="", tg-bo-title="file.name", tg-bo-bind="file.name") - .attachment-size - span.attachment-size(tg-bo-bind="file.size") - .attachment-comments - span(ng-bind="file.progressMessage") - .percentage(ng-style="{'width': file.progressPercent}") - - a.more-attachments(href="", title="{{'ATTACHMENT.SHOW_DEPRECATED' | translate}}", ng-if="ctrl.deprecatedAttachmentsCount > 0") - span.text(data-type="show", translate="ATTACHMENT.SHOW_DEPRECATED") - span.text.hidden(data-type="hide", translate="ATTACHMENT.HIDE_DEPRECATED") - span.more-attachments-num(translate="ATTACHMENT.COUNT_DEPRECATED", translate-values="{counter: '{{ctrl.deprecatedAttachmentsCount}}'}") - - div.lightbox.lightbox-block(tg-lb-attachment-preview) \ No newline at end of file diff --git a/app/partials/auth/login-text.jade b/app/partials/auth/login-text.jade index f5db9ef4..90b384f8 100644 --- a/app/partials/auth/login-text.jade +++ b/app/partials/auth/login-text.jade @@ -1,4 +1,4 @@ p.login-text span(translate="AUTH.NOT_REGISTERED_YET") |   - a(href!='<%- url %>', tg-nav='register', title='{{"AUTH.REGISTER" | translate}}', translate="AUTH.CREATE_ACCOUNT") + a(href!='<%- url %>', title='{{"AUTH.REGISTER" | translate}}', translate="AUTH.CREATE_ACCOUNT") diff --git a/app/partials/backlog/backlog.jade b/app/partials/backlog/backlog.jade index b55788b4..0752b02f 100644 --- a/app/partials/backlog/backlog.jade +++ b/app/partials/backlog/backlog.jade @@ -24,13 +24,22 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl", div.backlog-menu div.backlog-table-options - a.trans-button.move-to-current-sprint( + a.trans-button.move-to-current-sprint.move-to-sprint( + ng-if="currentSprint" 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.move-to-latest-sprint.move-to-sprint( + ng-if="!currentSprint" + href="" + title="{{'BACKLOG.MOVE_US_TO_LATEST_SPRINT' | translate}}" + id="move-to-latest-sprint" + ) + span.icon.icon-move + span.text(translate="BACKLOG.MOVE_US_TO_LATEST_SPRINT") a.trans-button( ng-if="userstories.length" href="" diff --git a/app/partials/common/components/add-button.jade b/app/partials/common/components/add-button.jade new file mode 100644 index 00000000..a510e0a1 --- /dev/null +++ b/app/partials/common/components/add-button.jade @@ -0,0 +1,4 @@ +a.add-button( + href="" +) + include ../../../svg/add.svg diff --git a/app/partials/common/components/assigned-to.jade b/app/partials/common/components/assigned-to.jade index 7ad68ab3..1c23647b 100644 --- a/app/partials/common/components/assigned-to.jade +++ b/app/partials/common/components/assigned-to.jade @@ -1,22 +1,44 @@ -<% if (assignedTo) { %> -.user-avatar - img(src!="<%- assignedTo.photo %>", alt!="<%- assignedTo.full_name_display %>") -<% } %> +.user-avatar(class!="<% if (isIocaine) { %> is-iocaine <% }; %>") + img(src!="<%- photo %>", alt!="<%- fullName %>") + <% if (isIocaine) { %> + .iocaine-symbol(title="{{ 'TASK.TITLE_ACTION_IOCAINE' | translate }}") + include ../../../svg/iocaine.svg + <% }; %> .assigned-to - span.assigned-title(translate="COMMON.FIELDS.ASSIGNED_TO") + <% if (isUnassigned) { %> + .assigned-title {{ "COMMON.ASSIGNED_TO.NOT_ASSIGNED" | translate }} + <% } else { %> + .assigned-title {{ "COMMON.FIELDS.ASSIGNED_TO" | translate }} + <% }; %> - a(href="" title="{{ 'COMMON.ASSIGNED_TO.TITLE_ACTION_EDIT_ASSIGNMENT'|translate }}", - class!="user-assigned <% if(isEditable){ %>editable<% }; %>") - span.assigned-name - <% if (assignedTo) { %> - <%- assignedTo.full_name_display %> - <% } else { %> - | {{ 'COMMON.ASSIGNED_TO.NOT_ASSIGNED'|translate }} - <% } %> - <% if(isEditable){ %> - span.icon.icon-arrow-bottom - <% }; %> - <% if (assignedTo!==null && isEditable) { %> - a.icon.icon-delete(href="" title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}") + .assigned-to-options + a( + href="" + title="{{ 'COMMON.ASSIGNED_TO.TITLE_ACTION_EDIT_ASSIGNMENT'|translate }}" + class!="user-assigned <% if (isEditable) { %>editable<% }; %>" + ) + span.assigned-name + <% if (isEditable && isUnassigned) { %> + <%- fullName %> + <% }; %> + <% if (isEditable && !isUnassigned) { %> + span.icon.icon-arrow-bottom + <% }; %> + + <% if (isEditable && isUnassigned) { %> + span(translate="COMMON.OR") + |   + a.assign-to-me( + href="#" + title="{{'COMMON.ASSIGNED_TO.SELF' | translate}}" + ) + span {{ "COMMON.ASSIGNED_TO.SELF" | translate }} + <% }; %> + + <% if (isEditable && !isUnassigned) { %> + a.icon.icon-delete( + href="" + title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}" + ) <% } %> diff --git a/app/partials/common/components/block-button.jade b/app/partials/common/components/block-button.jade index 8dc4e055..2f474602 100644 --- a/app/partials/common/components/block-button.jade +++ b/app/partials/common/components/block-button.jade @@ -1,4 +1,10 @@ -a(href="#", class="button button-gray item-block") - span(translate="COMMON.BLOCK") -a(href="#", class="button button-red item-unblock") - span(translate="COMMON.UNBLOCK") +a.button-gray.item-block( + href="" + title="{{ 'COMMON.BLOCK_TITLE' | translate }}" +) + include ../../../svg/lock.svg +a.button-red.item-unblock( + href="" + title="{{ 'COMMON.UNBLOCK_TITLE' | translate }}" +) + include ../../../svg/unlock.svg diff --git a/app/partials/common/components/created-by.jade b/app/partials/common/components/created-by.jade index 3689afef..8486d0fa 100644 --- a/app/partials/common/components/created-by.jade +++ b/app/partials/common/components/created-by.jade @@ -1,9 +1,17 @@ -.user-avatar - a(href="{{url}}", title="{{owner.full_name_display}}") - img(src="{{owner.photo}}", alt="{{owner.full_name_display}}") - .created-by - a(href="{{url}}", title="{{owner.full_name_display}}") - span.created-title(translate="COMMON.CREATED_BY", translate-values="{ 'fullDisplayName': owner.full_name_display}") - span.created-date - | {{date}} + a.created-title( + href="{{url}}" + title="{{owner.full_name_display}}" + translate="COMMON.CREATED_BY" + translate-values="{ 'fullDisplayName': owner.full_name_display}" + ) + .created-date {{date}} +.user-avatar + a( + href="{{url}}" + title="{{owner.full_name_display}}" + ) + img( + src="{{owner.photo}}" + alt="{{owner.full_name_display}}" + ) diff --git a/app/partials/common/components/delete-button.jade b/app/partials/common/components/delete-button.jade index cc325950..22f6ee5b 100644 --- a/app/partials/common/components/delete-button.jade +++ b/app/partials/common/components/delete-button.jade @@ -1,2 +1,5 @@ -a.button-red.button-delete(href="") - span(translate="COMMON.DELETE") +a.button-red.button-delete( + href="" + title="{{ 'COMMON.DELETE' | translate }}" +) + include ../../../svg/trash.svg diff --git a/app/partials/common/components/editable-description.jade b/app/partials/common/components/editable-description.jade index caa56bd6..6cfdeda1 100644 --- a/app/partials/common/components/editable-description.jade +++ b/app/partials/common/components/editable-description.jade @@ -1,11 +1,11 @@ +include wysiwyg.jade + .view-description section.us-content.wysiwyg(tg-bind-html="item.description_html || noDescriptionMsg") span.edit.icon.icon-edit .edit-description textarea(ng-attr-placeholder="{{'COMMON.DESCRIPTION.EMPTY' | translate}}", ng-model="item.description", tg-markitup="tg-markitup") - a.help-markdown(href="https://taiga.io/support/taiga-markdown-syntax/", target="_blank", title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") - span.icon.icon-help - span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") + +wysihelp span.save-container a.save.icon.icon-floppy(href="", title="{{'COMMON.SAVE' | translate}}") diff --git a/app/partials/common/components/editable-subject.jade b/app/partials/common/components/editable-subject.jade index f77ba862..f5d728bb 100644 --- a/app/partials/common/components/editable-subject.jade +++ b/app/partials/common/components/editable-subject.jade @@ -2,6 +2,6 @@ | {{ item.subject }} a.edit.icon.icon-edit(href="" title="{{'COMMON.EDIT' | translate}}") .edit-subject - input(type="text", ng-model="item.subject", data-required="true", data-maxlength="500") + input(type="text", ng-model="item.subject", data-required="true", data-maxlength="500", ng-model-options="{ debounce: 200 }") span.save-container a.save.icon.icon-floppy(href="", title="{{'COMMON.SAVE' | translate}}") diff --git a/app/partials/common/components/main-title.jade b/app/partials/common/components/main-title.jade index b5b1756f..27cbe21c 100644 --- a/app/partials/common/components/main-title.jade +++ b/app/partials/common/components/main-title.jade @@ -1,4 +1,4 @@ -span.project-name +span.project-name(ng-if="projectName") | {{ projectName }} span.green - | {{ sectionName }} + | {{ sectionName | translate }} diff --git a/app/partials/common/components/status-display.jade b/app/partials/common/components/status-display.jade index 098ffa32..553ea3b1 100644 --- a/app/partials/common/components/status-display.jade +++ b/app/partials/common/components/status-display.jade @@ -3,5 +3,3 @@ span(translate="COMMON.STATUS.CLOSED") <% } else { %> span(translate="COMMON.STATUS.OPEN") <% } %> -span(class="detail-status", style!="color:<%- status.color %>") - | <%- status.name %> diff --git a/app/partials/common/components/watchers.jade b/app/partials/common/components/watchers.jade index 31e4ea94..fcbe398d 100644 --- a/app/partials/common/components/watchers.jade +++ b/app/partials/common/components/watchers.jade @@ -18,12 +18,3 @@ <% }; %> <% } %> <% }); %> - -<% if(isEditable){ %> -a.add-watcher.js-add-watcher( - href="" - title="{{'COMMON.WATCHERS.TITLE_ADD' | translate}}" -) - span.icon.icon-plus - span(translate="COMMON.WATCHERS.ADD") -<% }; %> diff --git a/app/partials/common/components/wysiwyg.jade b/app/partials/common/components/wysiwyg.jade new file mode 100644 index 00000000..3d90e44a --- /dev/null +++ b/app/partials/common/components/wysiwyg.jade @@ -0,0 +1,6 @@ +mixin wysihelp + div.wysiwyg-help + span.drag-drop-help Attach files by dragging & dropping on the textarea above. + a.help-markdown(href="https://taiga.io/support/taiga-markdown-syntax/", target="_blank", title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") + span.icon.icon-help + span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") diff --git a/app/partials/common/estimation/us-estimation-points-per-role.jade b/app/partials/common/estimation/us-estimation-points-per-role.jade index 904d6b4a..86bf489c 100644 --- a/app/partials/common/estimation/us-estimation-points-per-role.jade +++ b/app/partials/common/estimation/us-estimation-points-per-role.jade @@ -1,9 +1,12 @@ ul.points-per-role + <% _.each(roles, function(role) { %> + li.ticket-role-points.total(class!="<% if(editable){ %>clickable<% } %>", data-role-id!="<%- role.id %>", title!="<%- role.name %>") + span.points + <%- role.points %> + span.icon-arrow-bottom + span.role + <%- role.name %> + <% }); %> li.ticket-role-points.total span.points <%- totalPoints %> span.role(translate="US.TOTAL_POINTS") - <% _.each(roles, function(role) { %> - li.ticket-role-points(class!="total <% if(editable){ %>clickable<% } %>", data-role-id!="<%- role.id %>", title!="<%- role.name %>") - span.points <%- role.points %> - span.role <%- role.name %> - <% }); %> diff --git a/app/partials/common/history/history-activity.jade b/app/partials/common/history/history-activity.jade index ee6152fa..a49c3fb6 100644 --- a/app/partials/common/history/history-activity.jade +++ b/app/partials/common/history/history-activity.jade @@ -18,7 +18,11 @@ div(class!="activity-single <%- mode %>") .comment.wysiwyg(ng-non-bindable) | <%= comment %> <% if (!deleteCommentDate && mode !== "activity" && canDeleteComment) { %> - a(href="", class="icon icon-delete comment-delete", data-activity-id!="<%- activityId %>") + a.icon.icon-delete.comment-delete( + href="" + data-activity-id!="<%- activityId %>" + title!="<%- deleteCommentActionTitle %>" + ) <% } %> <% } %> diff --git a/app/partials/common/history/history-base.jade b/app/partials/common/history/history-base.jade index 0cb8289a..5accec64 100644 --- a/app/partials/common/history/history-base.jade +++ b/app/partials/common/history/history-base.jade @@ -1,4 +1,6 @@ -section.history(tg-check-permission!="modify_<%- type %>") +include ../components/wysiwyg.jade + +section.history ul.history-tabs li a(href="#", class="active", data-section-class="history-comments") @@ -10,13 +12,12 @@ section.history(tg-check-permission!="modify_<%- type %>") span.tab-title(translate="ACTIVITY.TITLE") section.history-comments .comments-list - div(tg-toggle-comment, class="add-comment") - textarea(ng-attr-placeholder="{{'COMMENTS.TYPE_NEW_COMMENT' | translate}}", ng-model!="<%- ngmodel %>.comment", tg-markitup="tg-markitup") - <% if (mode !== "edit") { %> - a(class="help-markdown", href="https://taiga.io/support/taiga-markdown-syntax/", target="_blank", title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") - span.icon.icon-help - span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") - button(type="button", ng-disabled!="!<%- ngmodel %>.comment.length" title="{{'COMMENTS.COMMENT' | translate}}", translate="COMMENTS.COMMENT", class="button button-green save-comment") - <% } %> + div(tg-editable-wysiwyg, ng-model!="<%- ngmodel %>") + div(tg-check-permission!="modify_<%- type %>", tg-toggle-comment, class="add-comment") + textarea(ng-attr-placeholder="{{'COMMENTS.TYPE_NEW_COMMENT' | translate}}", ng-model!="<%- ngmodel %>.comment", tg-markitup="tg-markitup") + <% if (mode !== "edit") { %> + +wysihelp + button(type="button", ng-disabled!="!<%- ngmodel %>.comment.length" title="{{'COMMENTS.COMMENT' | translate}}", translate="COMMENTS.COMMENT", class="button button-green save-comment") + <% } %> section.history-activity.hidden .changes-list diff --git a/app/partials/common/history/history-deleted-comment.jade b/app/partials/common/history/history-deleted-comment.jade index 2e8cfe44..fbbcd29d 100644 --- a/app/partials/common/history/history-deleted-comment.jade +++ b/app/partials/common/history/history-deleted-comment.jade @@ -8,7 +8,11 @@ class="hide-deleted-comment hidden", translate="COMMENTS.HIDE_DELETED") .comment-body.wysiwyg <%= deleteComment %> <% if (canRestoreComment) { %> - a(href="", class="comment-restore", data-activity-id!="<%- activityId %>") + a.comment-restore( + href="" + data-activity-id!="<%- activityId %>" + title="{{ 'COMMENTS.RESTORE' | translate }}" + ) span.icon.icon-reload span(translate="COMMENTS.RESTORE") <% } %> diff --git a/app/partials/common/lightbox/lightbox-attachment-preview.jade b/app/partials/common/lightbox/lightbox-attachment-preview.jade index 91932ed7..b1b1ebb5 100644 --- a/app/partials/common/lightbox/lightbox-attachment-preview.jade +++ b/app/partials/common/lightbox/lightbox-attachment-preview.jade @@ -2,5 +2,5 @@ a.close(href="", title="{{'COMMON.CLOSE' | translate}}") span.icon.icon-delete - a(href!="<%- url %>", title!="<%- title %>", target="_blank", download!="<%- name %>") - img(src!="<%- url %>") \ No newline at end of file + a(href="{{::file.get('url')}}", title="{{::file.get('description')}}", target="_blank", download="{{::file.get('name')}}") + img(src="{{::file.get('url')}}") \ No newline at end of file diff --git a/app/partials/common/lightbox/lightbox-blocking-message-input.jade b/app/partials/common/lightbox/lightbox-blocking-message-input.jade index 3c1c84c8..68032eee 100644 --- a/app/partials/common/lightbox/lightbox-blocking-message-input.jade +++ b/app/partials/common/lightbox/lightbox-blocking-message-input.jade @@ -1,4 +1,7 @@ fieldset.blocked-note.hidden - textarea(name="blocked_note", - ng-attr-placeholder="{{'COMMON.BLOCKED_NOTE' | translate}}", - ng-model!="<%- ngmodel %>") + input( + type="text" + name="blocked_note" + ng-attr-placeholder="{{'COMMON.BLOCKED_NOTE' | translate}}" + ng-model!="<%- ngmodel %>" + ) diff --git a/app/partials/common/tag/tag-line.jade b/app/partials/common/tag/tag-line.jade index c7df1c3a..d6bc320a 100644 --- a/app/partials/common/tag/tag-line.jade +++ b/app/partials/common/tag/tag-line.jade @@ -4,4 +4,4 @@ a(href="#", class="add-tag hidden", title="{{'COMMON.TAGS.ADD' | translate}}") span.add-tag-text(translate="COMMON.TAGS.ADD") input(type="text", placeholder="{{'COMMON.TAGS.PLACEHOLDER' | translate}}", class="tag-input hidden") -a(href="", title="{{'COMMON.SAVE' | translate}}", class="save icon icon-floppy hidden") \ No newline at end of file +a(href="", title="{{'COMMON.SAVE' | translate}}", class="save icon icon-floppy hidden") diff --git a/app/partials/includes/components/mainTitle.jade b/app/partials/includes/components/mainTitle.jade index 8e7fa75d..24914a14 100644 --- a/app/partials/includes/components/mainTitle.jade +++ b/app/partials/includes/components/mainTitle.jade @@ -1,2 +1,6 @@ header - h1(tg-main-title, project-name="project.name", i18n-section-name="{{ sectionName }}") + h1( + tg-main-title + project-name="project.name" + i18n-section-name="{{ sectionName }}" + ) diff --git a/app/partials/includes/modules/lightbox-create-issue.jade b/app/partials/includes/modules/lightbox-create-issue.jade index dae01853..bfcd53dd 100644 --- a/app/partials/includes/modules/lightbox-create-issue.jade +++ b/app/partials/includes/modules/lightbox-create-issue.jade @@ -3,8 +3,7 @@ a.close(href="", title="{{'COMMON.CLOSE' | translate}}") form h2.title(translate="LIGHTBOX.CREATE_ISSUE.TITLE") fieldset - input(type="text", ng-model="issue.subject", ng-attr-placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}", - data-required="true", data-maxlength="500") + input(type="text", ng-model="issue.subject", ng-attr-placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}", ng-model-options="{ debounce: 200 }", data-required="true", data-maxlength="500") div.fieldset-row fieldset select.type(ng-model="issue.type", ng-options="t.id as t.name for t in issueTypes") @@ -16,6 +15,14 @@ form fieldset div.tags-block(tg-lb-tag-line, ng-model="issue.tags") + fieldset + section + tg-attachments-simple( + attachments="attachments", + on-add="addAttachment(attachment)" + on-delete="deleteAttachment(attachment)" + ) + fieldset textarea.description(ng-attr-placeholder="{{'COMMON.FIELDS.DESCRIPTION' | translate}}", ng-model="issue.description") diff --git a/app/partials/includes/modules/lightbox-sprint-add-edit.jade b/app/partials/includes/modules/lightbox-sprint-add-edit.jade index 7d9263cd..d44ad710 100644 --- a/app/partials/includes/modules/lightbox-sprint-add-edit.jade +++ b/app/partials/includes/modules/lightbox-sprint-add-edit.jade @@ -5,6 +5,7 @@ form h2.title(translate="LIGHTBOX.ADD_EDIT_SPRINT.TITLE") fieldset input.sprint-name(type="text", name="name", ng-model="sprint.name", + ng-model-options="{ debounce: 200 }", data-required="true", data-maxlength="500", placeholder="{{'LIGHTBOX.ADD_EDIT_SPRINT.PLACEHOLDER_SPRINT_NAME' | translate}}") label.last-sprint-name diff --git a/app/partials/includes/modules/lightbox-task-create-edit.jade b/app/partials/includes/modules/lightbox-task-create-edit.jade index 3c7c7ca7..a878e7ad 100644 --- a/app/partials/includes/modules/lightbox-task-create-edit.jade +++ b/app/partials/includes/modules/lightbox-task-create-edit.jade @@ -3,7 +3,7 @@ a.close(href="", title="{{'COMMON.CLOSE' | translate}}") form h2.title(translate="LIGHTBOX.CREATE_EDIT_TASK.TITLE") fieldset - input(type="text", ng-model="task.subject", ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SUBJECT' | translate}}", + input(type="text", ng-model="task.subject", ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SUBJECT' | translate}}", ng-model-options="{ debounce: 200 }", data-required="true", data-maxlength="500") fieldset @@ -19,7 +19,15 @@ form div.tags-block(tg-lb-tag-line, ng-model="task.tags") fieldset - textarea.description(ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SHORT_DESCRIPTION' | translate}}", ng-model="task.description") + section + tg-attachments-simple( + attachments="attachments", + on-add="addAttachment(attachment)" + on-delete="deleteAttachment(attachment)" + ) + + fieldset + textarea.description(ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SHORT_DESCRIPTION' | translate}}", ng-model="task.description", ng-model-options="{ debounce: 200 }") div.settings fieldset.iocaine-flag(title="{{'COMMON.IOCAINE_TEXT' | translate}}") diff --git a/app/partials/includes/modules/lightbox-us-create-edit.jade b/app/partials/includes/modules/lightbox-us-create-edit.jade index 98259aa3..bb3d5045 100644 --- a/app/partials/includes/modules/lightbox-us-create-edit.jade +++ b/app/partials/includes/modules/lightbox-us-create-edit.jade @@ -3,7 +3,7 @@ a.close(href="", title="{{'COMMON.CLOSE' | translate}}") form h2.title(translate="LIGHTBOX.CREATE_EDIT_US.TITLE") fieldset - input(type="text", name="subject", ng-model="us.subject", placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}", + input(type="text", name="subject", ng-model-options="{ debounce: 200 }", ng-model="us.subject", placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}", data-required="true", data-maxlength="500") fieldset.ticket-estimation @@ -16,8 +16,16 @@ form div.tags-block(tg-lb-tag-line, ng-model="us.tags") fieldset - textarea.description(name="description", ng-model="us.description", + textarea.description(name="description", ng-model="us.description", ng-model-options="{ debounce: 200 }", ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_US.PLACEHOLDER_DESCRIPTION' | translate}}") + fieldset + section + tg-attachments-simple( + attachments="attachments", + on-add="addAttachment(attachment)" + on-delete="deleteAttachment(attachment)" + ) + div.settings fieldset.team-requirement input(type="checkbox", name="team_requirement", ng-model="us.team_requirement", diff --git a/app/partials/includes/modules/register-form.jade b/app/partials/includes/modules/register-form.jade index 4d9d8c56..e5aa9b6b 100644 --- a/app/partials/includes/modules/register-form.jade +++ b/app/partials/includes/modules/register-form.jade @@ -4,7 +4,7 @@ div.register-form-container(tg-register) fieldset input( type="text" - autocorrect="off" + autocorrect="off" autocapitalize="none" name="username" ng-model="data.username" @@ -46,12 +46,17 @@ div.register-form-container(tg-register) ) fieldset - button.button-register.button-gray.submit-button( + button.button-register.button-green.submit-button( type="submit" title="{{'REGISTER_FORM.ACTION_SIGN_UP' | translate}}" translate="REGISTER_FORM.ACTION_SIGN_UP" ) + fieldset( + ng-repeat="plugin in contribPlugins|filter:{type: 'auth'}" + ng-include="plugin.template" + ) + //- Only displays terms notice when terms plugin is loaded. tg-terms-of-service-and-privacy-policy-notice @@ -59,5 +64,6 @@ div.register-form-container(tg-register) href="" title="{{'REGISTER_FORM.TITLE_LINK_LOGIN' | translate}}" tg-nav="login" + tg-nav-get-params="{\"next\": \"{{nextUrl}}\"}" translate="REGISTER_FORM.LINK_LOGIN" ) diff --git a/app/partials/includes/modules/related-tasks.jade b/app/partials/includes/modules/related-tasks.jade index 37efe89c..40c900b4 100644 --- a/app/partials/includes/modules/related-tasks.jade +++ b/app/partials/includes/modules/related-tasks.jade @@ -1,8 +1,12 @@ section.related-tasks(tg-related-tasks) - div.related-tasks-header + .related-tasks-header span.related-tasks-title(translate="COMMON.RELATED_TASKS") div(tg-related-task-create-button) - div.related-tasks-body - div.row.single-related-task(ng-repeat="task in tasks", ng-class="{closed: task.is_closed, blocked: task.is_blocked, iocaine: task.is_iocaine}", - tg-related-task-row, ng-model="task") - div.row.single-related-task.related-task-create-form(tg-related-task-create-form) + .related-tasks-body + .row.single-related-task( + ng-repeat="task in tasks" + ng-class="{closed: task.is_closed, blocked: task.is_blocked, iocaine: task.is_iocaine}" + tg-related-task-row + ng-model="task" + ) + .row.single-related-task.related-task-create-form(tg-related-task-create-form) diff --git a/app/partials/includes/modules/search-filter.jade b/app/partials/includes/modules/search-filter.jade index 22cb8f89..5c8c93ae 100644 --- a/app/partials/includes/modules/search-filter.jade +++ b/app/partials/includes/modules/search-filter.jade @@ -1,30 +1,36 @@ -section.search-filter - ul - li.userstories(data-name="userstories") - a.active(href="#") - span.icon.icon-bulk - span.num 0 - span.name(translate="SEARCH.FILTER_USER_STORIES") +ul.search-filter + li.userstories(data-name="userstories") + a.active( + href="#" + title="{{ 'SEARCH.FILTER_USER_STORIES' | translate }}" + ) + span.icon.icon-bulk + span.num + span.name(translate="SEARCH.FILTER_USER_STORIES") - li.issues(data-name="issues") - a(href="#") - span.icon.icon-issues - span.num 0 - span.name(translate="SEARCH.FILTER_ISSUES") + li.issues(data-name="issues") + a( + href="#" + title="{{ 'SEARCH.FILTER_ISSUES' | translate }}" + ) + span.icon.icon-issues + span.num + span.name(translate="SEARCH.FILTER_ISSUES") - li.tasks(data-name="tasks") - a(href="#") - span.icon.icon-bulk - span.num 0 - span.name(translate="SEARCH.FILTER_TASKS") + li.tasks(data-name="tasks") + a( + href="#" + title="{{ 'SEARCH.FILTER_TASKS' | translate }}" + ) + span.icon.icon-bulk + span.num + span.name(translate="SEARCH.FILTER_TASKS") - li.wikipages(data-name="wikipages") - a(href="#") - span.icon.icon-wiki - span.num 0 - span.name(translate="SEARCH.FILTER_WIKI") - - //- li - //- a(href="#") - //- span.icon.icon-edit - //- | 3 Users + li.wikipages(data-name="wikipages") + a( + href="#" + title="{{ 'SEARCH.FILTER_WIKI' | translate }}" + ) + span.icon.icon-wiki + span.num + span.name(translate="SEARCH.FILTER_WIKI") diff --git a/app/partials/includes/modules/sprint.jade b/app/partials/includes/modules/sprint.jade index 29d229ac..f4460502 100644 --- a/app/partials/includes/modules/sprint.jade +++ b/app/partials/includes/modules/sprint.jade @@ -3,7 +3,9 @@ header(tg-backlog-sprint-header, ng-model="sprint") div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points") div.sprint-table - div.sprint-empty(ng-if="!sprint.user_stories.length") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT' | translate }} + div.sprint-empty(ng-if="!sprint.user_stories.length") + span(tg-class-permission="{'hidden': 'modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT_ANONYMOUS' | translate }} + span(tg-class-permission="{'hidden': '!modify_us'}") {{ 'BACKLOG.SPRINTS.WARNING_EMPTY_SPRINT' | translate }} div.row.milestone-us-item-row( ng-repeat="us in sprint.user_stories track by us.id" tg-bind-scope @@ -19,7 +21,7 @@ div.sprint-table div.column-points.width-1(tg-bo-bind="us.total_points", ng-class="{closed: us.is_closed, blocked: us.is_blocked}") -a.button-gray(title="{{ 'BACKLOG.SPRINTS.TITLE_LINK_TASKBOARD' | translate:sprint }}", +a.button-gray(title="{{ 'BACKLOG.SPRINTS.TITLE_LINK_TASKBOARD' | translate: {\"name\": sprint.name} }}", tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug", tg-check-permission="view_milestones") diff --git a/app/partials/issue/iocaine-button.jade b/app/partials/issue/iocaine-button.jade new file mode 100644 index 00000000..fecc54d9 --- /dev/null +++ b/app/partials/issue/iocaine-button.jade @@ -0,0 +1,11 @@ +fieldset(title="{{ 'TASK.TITLE_ACTION_IOCAINE' | translate }}") + label.button-gray.is-iocaine( + for="is-iocaine" + class!="<% if(isEditable){ %>is-editable<% }; %> <% if(isIocaine){ %>active<% }; %>" + ) + include ../../svg/iocaine.svg + input( + type="checkbox" + id="is-iocaine" + name="is-iocaine" + ) diff --git a/app/partials/issue/issues-detail.jade b/app/partials/issue/issues-detail.jade index 397bb6fc..da3a33e5 100644 --- a/app/partials/issue/issues-detail.jade +++ b/app/partials/issue/issues-detail.jade @@ -57,10 +57,11 @@ div.wrapper( tg-bo-href="nextUrl" title="{{'ISSUES.TITLE_NEXT_ISSUE' | translate}}" ) + .subheader + div.tags-block(tg-tag-line, ng-model="issue", required-perm="modify_issue") + tg-created-by-display.ticket-created-by(ng-model="issue") - div.tags-block(tg-tag-line, ng-model="issue", required-perm="modify_issue") - - section.duty-content(tg-editable-description, ng-model="issue", required-perm="modify_issue") + section.duty-content(tg-editable-description, tg-editable-wysiwyg, ng-model="issue", required-perm="modify_issue") // Custom Fields tg-custom-attributes-values( @@ -70,39 +71,58 @@ div.wrapper( required-edition-perm="modify_issue" ) - tg-attachments(ng-model="issue", type="issue") + tg-attachments-full( + obj-id="issue.id" + type="issue", + project-id="projectId" + ) + tg-history(ng-model="issue", type="issue") sidebar.menu-secondary.sidebar.ticket-data - section.status - .ticket-title(tg-issue-status-display, ng-model="issue") - tg-created-by-display.ticket-created-by(ng-model="issue") + + .ticket-header + span.ticket-title( + tg-issue-status-display + ng-model="issue" + ) + span.detail-status( + tg-issue-status-button + ng-model="issue" + ) + + //tg-created-by-display.ticket-created-by(ng-model="issue") + div.ticket-data-container div.ticket-status(tg-issue-type-button, ng-model="issue") div.ticket-status(tg-issue-severity-button, ng-model="issue") div.ticket-status(tg-issue-priority-button, ng-model="issue") - div.ticket-status(tg-issue-status-button, ng-model="issue") section.ticket-assigned-to(tg-assigned-to, ng-model="issue", required-perm="modify_issue") - section.track-buttons-container.ticket-track-buttons - - div.watch-button - tg-watch-button( - item="issue" - on-watch="ctrl.onWatch" - on-unwatch="ctrl.onUnwatch" - ) - + section.ticket-watch-buttons + div.ticket-watch( + tg-watch-button + item="issue" + data-environment="ticket" + on-watch="ctrl.onWatch" + on-unwatch="ctrl.onUnwatch" + ) div.ticket-watchers( tg-watchers ng-model="issue" - required-perm="modify_issue" + required-perm="modify_us" ) section.ticket-detail-settings - tg-promote-issue-to-us-button(tg-check-permission="add_us", ng-model="issue") - tg-block-button(tg-check-permission="modify_issue", ng-model="issue") + tg-promote-issue-to-us-button( + tg-check-permission="add_us", + ng-model="issue" + ) + tg-block-button( + tg-check-permission="modify_issue", + ng-model="issue" + ) tg-delete-button( tg-check-permission="delete_issue", on-delete-title="{{'ISSUES.ACTION_DELETE' | translate}}", diff --git a/app/partials/issue/issues-status-button.jade b/app/partials/issue/issues-status-button.jade index 6d56fae8..957831a8 100644 --- a/app/partials/issue/issues-status-button.jade +++ b/app/partials/issue/issues-status-button.jade @@ -1,15 +1,20 @@ -div(class!="status-data <% if(editable){ %>clickable<% }%>") - span(class="level", style!="background-color:<%- status.color %>") - span(class="status-status") <%- status.name %> +span.detail-status-inner.js-edit-status( + class!="<% if(editable){ %>clickable<% }%>" + style!="background-color:<%- status.color %>" + ng-click="editStatus()" +) + span <%- status.name %> <% if(editable){ %> - span(class="icon icon-arrow-bottom") - <% } %> - span(class="level-name", translate="COMMON.FIELDS.STATUS") + span.icon.icon-arrow-bottom + <% }%> - ul(class="popover pop-status") + ul.popover.pop-status <% _.each(statuses, function(st) { %> li - a(href="", class="status", title!="<%- st.name %>", - data-status-id!="<%- st.id %>") + a.status( + href="" + title!="<%- st.name %>" + data-status-id!="<%- st.id %>" + ) | <%- st.name %> <% }); %> diff --git a/app/partials/issue/issues.jade b/app/partials/issue/issues.jade index cee3d14a..70f2f701 100644 --- a/app/partials/issue/issues.jade +++ b/app/partials/issue/issues.jade @@ -1,6 +1,6 @@ doctype html -div.wrapper.issues(tg-issues, ng-controller="IssuesController as ctrl", ng-init="section='issues'") +div.wrapper.issues.lightbox-generic-form(tg-issues, ng-controller="IssuesController as ctrl", ng-init="section='issues'") tg-project-menu sidebar.menu-secondary.extrabar.filters-bar(tg-issues-filters) include ../includes/modules/issues-filters diff --git a/app/partials/issue/promote-issue-to-us-button.jade b/app/partials/issue/promote-issue-to-us-button.jade index 6d56172b..d9a4ad31 100644 --- a/app/partials/issue/promote-issue-to-us-button.jade +++ b/app/partials/issue/promote-issue-to-us-button.jade @@ -1,2 +1,5 @@ -a(class="button button-gray editable", tg-check-permission="add_us") - span(translate="ISSUES.ACTION_PROMOTE_TO_US") +a.promote-button.editable( + tg-check-permission="add_us" + title="{{ 'ISSUES.ACTION_PROMOTE_TO_US' | translate }}" +) + include ../../svg/promote.svg diff --git a/app/partials/task/related-task-row.jade b/app/partials/task/related-task-row.jade index bbef4fd6..0a51af0d 100644 --- a/app/partials/task/related-task-row.jade +++ b/app/partials/task/related-task-row.jade @@ -1,27 +1,46 @@ -div(class="tasks") - div(class="task-name") - span(class="icon icon-iocaine") - a(tg-nav="project-tasks-detail:project=project.slug,ref=task.ref" title!="#<%- task.ref %> <%- task.subject %>" class="clickable") - span #<%- task.ref %>  +.tasks + .task-name + .icon.icon-iocaine + a.clickable( + tg-nav="project-tasks-detail:project=project.slug,ref=task.ref" + title!="#<%- task.ref %> <%- task.subject %>") + span #<%- task.ref %> span <%- task.subject %> - div(class="task-settings") + .task-settings <% if(perms.modify_task) { %> - a(href="" title="{{'COMMON.EDIT' | translate}}" class="icon icon-edit") + a.icon.icon-edit( + href="" + title="{{'COMMON.EDIT' | translate}}" + ) <% } %> <% if(perms.delete_task) { %> - a(href="" title="{{'COMMON.DELETE' | translate}}" class="icon icon-delete delete-task") + a.icon.icon-delete.delete-task( + href="" + title="{{'COMMON.DELETE' | translate}}" + ) <% } %> -div(tg-related-task-status="task" ng-model="task" class="status") - a(href="" title="{{'TASK.TITLE_SELECT_STATUS' | translate}}" class="task-status") - span(class="task-status-bind") +.status( + tg-related-task-status="task" + ng-model="task" +) + a.task-status( + href="" + title="{{'TASK.TITLE_SELECT_STATUS' | translate}}" + ) + span.task-status-bind <% if(perms.modify_task) { %> - span(class="icon icon-arrow-bottom") + span.icon.icon-arrow-bottom <% } %> -div(tg-related-task-assigned-to-inline-edition="task" class="assigned-to") - div(title="{{'COMMON.FIELDS.ASSIGNED_TO' | translate}}" class="task-assignedto <% if(perms.modify_task) { %>editable<% } %>") - figure(class="avatar") +.assigned-to( + tg-related-task-assigned-to-inline-edition="task" +) + .task-assignedto( + title="{{'COMMON.FIELDS.ASSIGNED_TO' | translate}}" + class="<% if(perms.modify_task) { %>editable<% } %>" + ) + figure.avatar <% if(perms.modify_task) { %> - span(class="icon icon-arrow-bottom") + span.icon.icon-arrow-bottom <% } %> diff --git a/app/partials/task/task-detail.jade b/app/partials/task/task-detail.jade index 1f732574..f9570518 100644 --- a/app/partials/task/task-detail.jade +++ b/app/partials/task/task-detail.jade @@ -72,10 +72,11 @@ div.wrapper( tg-bo-href="nextUrl" title="{{'TASK.NEXT' | translate}}" ) + .subheader + div.tags-block(tg-tag-line, ng-model="task", required-perm="modify_task") + tg-created-by-display.ticket-created-by(ng-model="task") - div.tags-block(tg-tag-line, ng-model="task", required-perm="modify_task") - - section.duty-content(tg-editable-description, ng-model="task", required-perm="modify_task") + section.duty-content(tg-editable-description, tg-editable-wysiwyg, ng-model="task", required-perm="modify_task") // Custom Fields tg-custom-attributes-values( @@ -85,34 +86,40 @@ div.wrapper( required-edition-perm="modify_task" ) - tg-attachments(ng-model="task", type="task") + tg-attachments-full( + obj-id="task.id" + type="task", + project-id="projectId" + ) + tg-history(ng-model="task", type="task") sidebar.menu-secondary.sidebar.ticket-data - section.status - - .ticket-title(tg-task-status-display, ng-model="task") - - .ticket-created-by(tg-created-by-display, ng-model="task") - - .ticket-data-container - .ticket-status(tg-task-status-button, ng-model="task") + .ticket-header + span.ticket-title( + tg-task-status-display + ng-model="task" + ) + span.detail-status( + tg-task-status-button + ng-model="task" + ) section.ticket-assigned-to(tg-assigned-to, ng-model="task", required-perm="modify_task") - section.track-buttons-container.ticket-track-buttons - div.watch-button - tg-watch-button( - item="task" - on-watch="ctrl.onWatch" - on-unwatch="ctrl.onUnwatch" - ) - + section.ticket-watch-buttons + div.ticket-watch( + tg-watch-button + item="task" + data-environment="ticket" + on-watch="ctrl.onWatch" + on-unwatch="ctrl.onUnwatch" + ) div.ticket-watchers( - tg-watchers, - ng-model="task", - required-perm="modify_task" + tg-watchers + ng-model="task" + required-perm="modify_us" ) section.ticket-detail-settings diff --git a/app/partials/us/us-client-requirement-button.jade b/app/partials/us/us-client-requirement-button.jade index dc269cbc..b8d7a506 100644 --- a/app/partials/us/us-client-requirement-button.jade +++ b/app/partials/us/us-client-requirement-button.jade @@ -1,4 +1,11 @@ -label(for="client-requirement", - class!="button button-gray client-requirement <% if(canEdit){ %>editable<% }; %> <% if(isRequired){ %>active<% }; %>", translate="US.FIELDS.CLIENT_REQUIREMENT") - -input(type="checkbox", id="client-requirement", name="client-requirement") \ No newline at end of file +label.button-gray.client-requirement( + for="client-requirement" + class!="<% if(canEdit){ %>is-editable<% }; %> <% if(isRequired){ %>active<% }; %>" + title="{{ 'COMMON.CLIENT_REQUIREMENT' | translate }}" +) + include ../../svg/client-requirement.svg +input( + type="checkbox" + id="client-requirement" + name="client-requirement" +) diff --git a/app/partials/us/us-detail.jade b/app/partials/us/us-detail.jade index f3606df8..65b6f893 100644 --- a/app/partials/us/us-detail.jade +++ b/app/partials/us/us-detail.jade @@ -65,10 +65,11 @@ div.wrapper( tg-bo-href="nextUrl" title="{{'US.NEXT' | translate}}" ) + .subheader + .tags-block(tg-tag-line, ng-model="us", required-perm="modify_us") + tg-created-by-display.ticket-created-by(ng-model="us") - div.tags-block(tg-tag-line, ng-model="us", required-perm="modify_us") - - section.duty-content(tg-editable-description, ng-model="us", required-perm="modify_us") + section.duty-content(tg-editable-description, tg-editable-wysiwyg, ng-model="us", required-perm="modify_us") // Custom Fields tg-custom-attributes-values( @@ -80,34 +81,30 @@ div.wrapper( include ../includes/modules/related-tasks - tg-attachments( - ng-model="us" - type="us" + tg-attachments-full( + obj-id="us.id" + type="us", + project-id="projectId" ) + tg-history( ng-model="us" type="us" ) sidebar.menu-secondary.sidebar.ticket-data - section - div.ticket-title( + + section.ticket-header + span.ticket-title( tg-us-status-display ng-model="us" ) + span.detail-status( + tg-us-status-button + ng-model="us" + ) - tg-created-by-display.ticket-created-by(ng-model="us") - - //div.ticket-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks") - - div.ticket-data-container - div.ticket-status( - tg-us-status-button - ng-model="us" - ) - - section.ticket-estimation - tg-us-estimation(ng-model="us") + tg-us-estimation.ticket-estimation(ng-model="us") section.ticket-assigned-to( tg-assigned-to @@ -115,20 +112,21 @@ div.wrapper( required-perm="modify_us" ) - section.track-buttons-container.ticket-track-buttons - div.watch-button - tg-watch-button( - item="us" - on-watch="ctrl.onWatch" - on-unwatch="ctrl.onUnwatch" - ) - + section.ticket-watch-buttons + div.ticket-watch( + tg-watch-button + item="us" + data-environment="ticket" + on-watch="ctrl.onWatch" + on-unwatch="ctrl.onUnwatch" + ) div.ticket-watchers( tg-watchers ng-model="us" required-perm="modify_us" ) + section.ticket-detail-settings tg-us-team-requirement-button(ng-model="us") tg-us-client-requirement-button(ng-model="us") diff --git a/app/partials/us/us-status-button.jade b/app/partials/us/us-status-button.jade index 7a3ba3b5..64f9caaf 100644 --- a/app/partials/us/us-status-button.jade +++ b/app/partials/us/us-status-button.jade @@ -1,14 +1,19 @@ -div(class!="status-data <% if(editable){ %>clickable<% }%>") - span.level(style!="background-color:<%- status.color %>") - span.status-status <%- status.name %> +span.detail-status-inner.js-edit-status( + class!="<% if(editable){ %>clickable<% }%>" + style!="background-color:<%- status.color %>" +) + span <%- status.name %> <% if(editable){ %> span.icon.icon-arrow-bottom <% }%> - span.level-name(translate="COMMON.FIELDS.STATUS") - ul.popover.pop-status + ul.pop-status.popover <% _.each(statuses, function(st) { %> li - a(href="", class="status", title!="<%- st.name %>", data-status-id!="<%- st.id %>") + a.status( + href="" + title!="<%- st.name %>" + data-status-id!="<%- st.id %>" + ) | <%- st.name %> <% }); %> diff --git a/app/partials/us/us-task-progress.jade b/app/partials/us/us-task-progress.jade deleted file mode 100644 index df2f8b7c..00000000 --- a/app/partials/us/us-task-progress.jade +++ /dev/null @@ -1,2 +0,0 @@ -.current-progress(ng-style='style') -div.tasks-completed(translate="US.TASK_COMPLETED", translate-values='{ totalTasks: totalTasks, totalClosedTasks: totalClosedTasks}') diff --git a/app/partials/us/us-team-requirement-button.jade b/app/partials/us/us-team-requirement-button.jade index e1520364..e34338b1 100644 --- a/app/partials/us/us-team-requirement-button.jade +++ b/app/partials/us/us-team-requirement-button.jade @@ -1,3 +1,12 @@ -label(for="team-requirement", class!="button button-gray team-requirement <% if(canEdit){ %>editable<% }; %> <% if(isRequired){ %>active<% }; %>", translate="US.FIELDS.TEAM_REQUIREMENT") +label.button-gray.team-requirement( + for="team-requirement" + class!=" <% if(canEdit){ %>is-editable<% }; %> <% if(isRequired){ %>active<% }; %>" + title="{{ 'COMMON.TEAM_REQUIREMENT' | translate }}" +) + include ../../svg/team-requirement.svg -input(type="checkbox", id="team-requirement", name="team-requirement") \ No newline at end of file +input( + type="checkbox" + id="team-requirement" + name="team-requirement" +) diff --git a/app/partials/user/mail-notifications.jade b/app/partials/user/mail-notifications.jade index 35485362..2f4a5986 100644 --- a/app/partials/user/mail-notifications.jade +++ b/app/partials/user/mail-notifications.jade @@ -1,8 +1,10 @@ doctype html -div.wrapper(tg-user-notifications, ng-controller="UserNotificationsController as ctrl", - ng-init="section='mail-notifications'") - tg-project-menu +div.wrapper( + tg-user-notifications + ng-controller="UserNotificationsController as ctrl", + ng-init="section='mail-notifications'" +) sidebar.menu-secondary.sidebar.settings-nav(tg-user-settings-navigation="mail-notifications") include ../includes/modules/user-settings-menu diff --git a/app/partials/user/user-change-password.jade b/app/partials/user/user-change-password.jade index 734005c9..5b5e0b5d 100644 --- a/app/partials/user/user-change-password.jade +++ b/app/partials/user/user-change-password.jade @@ -5,8 +5,6 @@ div.wrapper( ng-controller="UserChangePasswordController as ctrl" ng-init="section='user-settings'" ) - tg-project-menu - sidebar.menu-secondary.sidebar.settings-nav(tg-user-settings-navigation="change-password") include ../includes/modules/user-settings-menu diff --git a/app/partials/user/user-profile.jade b/app/partials/user/user-profile.jade index 7064ba34..8cab43ea 100644 --- a/app/partials/user/user-profile.jade +++ b/app/partials/user/user-profile.jade @@ -1,126 +1,129 @@ doctype html -div.wrapper(tg-user-profile, ng-controller="UserSettingsController as ctrl", - ng-init="section='user-settings'") +div.wrapper( + tg-user-profile + ng-controller="UserSettingsController as ctrl" + ng-init="section='user-settings'" +) sidebar.menu-secondary.sidebar.settings-nav(tg-user-settings-navigation="user-profile") include ../includes/modules/user-settings-menu section.main.user-profile header - h1 - span.green {{sectionName | translate}} + include ../includes/components/mainTitle form - div.container - div.avatar-container - fieldset(tg-user-avatar) - .image-container - img.avatar(ng-src="{{user.big_photo}}" alt="avatar") - .overlay.hidden - img.loading-spinner( - src="/#{v}/svg/spinner-circle.svg", - alt="{{'COMMON.LOADING' | translate}}" - ) - - input.hidden( - type="file" - id="avatar-field" - tg-avatar-model="avatarAttachment" + .project-details-image(tg-user-avatar) + fieldset.image-container + img.image(ng-src="{{user.big_photo}}" alt="avatar") + .loading-overlay + img.loading-spinner( + src="/#{v}/svg/spinner-circle.svg", + alt="{{'COMMON.LOADING' | translate}}" ) + input.hidden( + type="file" + id="avatar-field" + tg-avatar-model="avatarAttachment" + ) + p.image-help + span {{ 'USER_PROFILE.IMAGE_HELP' | translate }} + span.size-info.hidden(tg-bo-html="maxFileSizeMsg") - p(translate="USER_PROFILE.IMAGE_HELP") - span.size-info.hidden(tg-bo-html="maxFileSizeMsg") + a.button-green.change.js-change-avatar( + href="#" + title="{{'USER_PROFILE.CHANGE_PHOTO' | translate}}" + ) {{'USER_PROFILE.CHANGE_PHOTO' | translate}} - a.button-green.change.js-change-avatar( - translate="USER_PROFILE.ACTION_CHANGE_IMAGE", - title="{{'USER_PROFILE.CHANGE_PHOTO' | translate}} {{maxFileSizeMsg}}" - ) - a.use-gravatar(translate="USER_PROFILE.ACTION_USE_GRAVATAR") + a.use-default-image.js-use-gravatar( + href="#" + title="{{ 'USER_PROFILE.ACTION_USE_GRAVATAR' | translate }}" + ) {{ 'USER_PROFILE.ACTION_USE_GRAVATAR' | translate }} - div.data - fieldset - label(for="username", translate="USER_PROFILE.FIELD.USERNAME") - input( - type="text" - autocorrect="off" - autocapitalize="none" - name="username" - id="username" - ng-model="user.username" - data-required="true" - data-maxlength="255" - data-regexp="^[\\w.-]+$" - placeholder="{{'USER_PROFILE.FIELD.USERNAME' | translate}}", - ) + .project-details-form-data + fieldset + label(for="username", translate="USER_PROFILE.FIELD.USERNAME") + input( + type="text" + autocorrect="off" + autocapitalize="none" + name="username" + id="username" + ng-model="user.username" + data-required="true" + data-maxlength="255" + data-regexp="^[\\w.-]+$" + placeholder="{{'USER_PROFILE.FIELD.USERNAME' | translate}}", + ) - fieldset - label(for="email", translate="USER_PROFILE.FIELD.EMAIL") - input( - type="email" - name="email" - id="email" - ng-model="user.email" - data-type="email" - data-required="true" - data-maxlength="255" - placeholder="{{'USER_PROFILE.FIELD.EMAIL' | translate}}" - ) + fieldset + label(for="email", translate="USER_PROFILE.FIELD.EMAIL") + input( + type="email" + name="email" + id="email" + ng-model="user.email" + data-type="email" + data-required="true" + data-maxlength="255" + placeholder="{{'USER_PROFILE.FIELD.EMAIL' | translate}}" + ) - fieldset - label(for="full-name", translate="USER_PROFILE.FIELD.FULL_NAME") - input( - type="text" - name="full_name" - id="full-name" - ng-model="user.full_name" - data-required="true" - data-maxlength="256" - placeholder="{{'USER_PROFILE.FIELD.PLACEHOLDER_FULL_NAME' | translate}}", - ) + fieldset + label(for="full-name", translate="USER_PROFILE.FIELD.FULL_NAME") + input( + type="text" + name="full_name" + id="full-name" + ng-model="user.full_name" + data-required="true" + data-maxlength="256" + placeholder="{{'USER_PROFILE.FIELD.PLACEHOLDER_FULL_NAME' | translate}}", + ) - fieldset - label(for="lang", translate="USER_PROFILE.FIELD.LANGUAGE") - select( - name="lang" - id="lang" - ng-model="lang" - ng-options="locale.code as locale.name for locale in locales" - ) - option(value="", translate="USER_PROFILE.FIELD.LANGUAGE_DEFAULT") + fieldset + label(for="lang", translate="USER_PROFILE.FIELD.LANGUAGE") + select( + name="lang" + id="lang" + ng-model="lang" + ng-options="locale.code as locale.name for locale in locales" + ) + option(value="", translate="USER_PROFILE.FIELD.LANGUAGE_DEFAULT") - fieldset - label(for="theme", translate="USER_PROFILE.FIELD.THEME") - select( - name="theme" - id="theme" - ng-model="theme" - ng-options="availableTheme for availableTheme in availableThemes" - ) - option(value="", translate="USER_PROFILE.FIELD.THEME_DEFAULT") + fieldset + label(for="theme", translate="USER_PROFILE.FIELD.THEME") + select( + name="theme" + id="theme" + ng-model="theme" + ng-options="availableTheme for availableTheme in availableThemes" + ) + option(value="", translate="USER_PROFILE.FIELD.THEME_DEFAULT") - fieldset - label(for="bio", translate="USER_PROFILE.FIELD.BIO") - textarea( - name="bio" - id="bio" - ng-model="user.bio" - ng-attr-placeholder="{{'USER_PROFILE.FIELD.PLACEHOLDER_BIO' | translate}}" - ng-maxlength="210" - maxlength="210" - ) + fieldset + label(for="bio", translate="USER_PROFILE.FIELD.BIO") + textarea( + name="bio" + id="bio" + ng-model="user.bio" + ng-attr-placeholder="{{'USER_PROFILE.FIELD.PLACEHOLDER_BIO' | translate}}" + ng-maxlength="210" + maxlength="210" + ) - fieldset.submit - button.button-green.submit-button( - type="submit" - title="{{'COMMON.SAVE' | translate}}", - translate="COMMON.SAVE" - ) - a.delete-account( - href="" - title="{{'USER_PROFILE.ACTION_DELETE_ACCOUNT' | translate}}" - ng-click="ctrl.openDeleteLightbox()" - translate="USER_PROFILE.ACTION_DELETE_ACCOUNT" - ) + fieldset.submit + button.button-green.submit-button( + type="submit" + title="{{'COMMON.SAVE' | translate}}", + translate="COMMON.SAVE" + ) + a.delete-account( + href="" + title="{{'USER_PROFILE.ACTION_DELETE_ACCOUNT' | translate}}" + ng-click="ctrl.openDeleteLightbox()" + translate="USER_PROFILE.ACTION_DELETE_ACCOUNT" + ) div.lightbox.lightbox-delete-account(tg-lb-delete-user) diff --git a/app/partials/wiki/editable-wiki-content.jade b/app/partials/wiki/editable-wiki-content.jade index 0aa56352..2faf3b51 100644 --- a/app/partials/wiki/editable-wiki-content.jade +++ b/app/partials/wiki/editable-wiki-content.jade @@ -1,3 +1,5 @@ +include ../common/components/wysiwyg.jade + .view-wiki-content section.wysiwyg(tg-bind-html='wiki.html') span.edit.icon.icon-edit(title="{{'COMMON.EDIT' | translate}}", ng-if="wiki") @@ -5,11 +7,7 @@ .edit-wiki-content(style='display: none;') textarea(ng-attr-placeholder="{{'WIKI.PLACEHOLDER_PAGE' | translate}}", ng-model='wiki.content', tg-markitup='tg-markitup') - - a.help-markdown(href='https://taiga.io/support/taiga-markdown-syntax/', target='_blank', - title="{{'COMMON.WYSIWYG.MARKDOWN_HELP' | translate}}") - span.icon.icon-help - span(translate="COMMON.WYSIWYG.MARKDOWN_HELP") + +wysihelp span.action-container a.save.icon.icon-floppy(href='', title="{{'COMMON.SAVE' | translate}}") diff --git a/app/partials/wiki/wiki.jade b/app/partials/wiki/wiki.jade index cadf21d9..40f525d4 100644 --- a/app/partials/wiki/wiki.jade +++ b/app/partials/wiki/wiki.jade @@ -16,7 +16,12 @@ div.wrapper(ng-controller="WikiDetailController as ctrl", h2.wiki-title(ng-bind='wikiTitle') section.wiki-content(tg-editable-wiki-content, ng-model="wiki") - tg-attachments(ng-model="wiki", type="wiki_page", ng-if="wiki.id") + tg-attachments-full( + ng-if="wiki.id" + obj-id="wiki.id" + type="wiki_page", + project-id="projectId" + ) a.remove(href="", ng-click="ctrl.delete()", ng-if="wiki.id", title="{{'WIKI.REMOVE' | translate}}", tg-check-permission="delete_wiki_page") span.icon.icon-delete diff --git a/app/styles/components/buttons.scss b/app/styles/components/buttons.scss index 30817476..e4f13781 100755 --- a/app/styles/components/buttons.scss +++ b/app/styles/components/buttons.scss @@ -4,6 +4,7 @@ @extend %small; background: transparent; border: 0; + border-radius: 3px; color: $white; cursor: pointer; display: inline-block; diff --git a/app/styles/components/created-by.scss b/app/styles/components/created-by.scss index 734bd5f9..60d6470a 100644 --- a/app/styles/components/created-by.scss +++ b/app/styles/components/created-by.scss @@ -1,29 +1,45 @@ .ticket-created-by { display: flex; - margin-bottom: 1rem; - margin-top: .5rem; - position: relative; + justify-content: flex-end; + @include breakpoint(laptop) { + justify-content: flex-start; + margin-bottom: .5rem; + } .user-avatar { - flex-basis: 3rem; + flex-basis: 2rem; flex-grow: 0; - margin-right: .5rem; + margin-left: .5rem; + @include breakpoint(laptop) { + margin-left: 0; + margin-right: .5rem; + order: 1; + } img { width: 100%; } } .created-by { + @include breakpoint(laptop) { + order: 2; + } .created-title, .created-date { @extend %light; @extend %small; color: $gray; display: block; - line-height: 1.5; + text-align: right; + @include breakpoint(laptop) { + text-align: left; + } } .created-title { color: $primary; - &.editable { - cursor: pointer; + } + .created-date { + margin-left: .5rem; + @include breakpoint(laptop) { + margin-left: 0; } } } diff --git a/app/styles/components/editor-help.scss b/app/styles/components/editor-help.scss new file mode 100644 index 00000000..64880b9a --- /dev/null +++ b/app/styles/components/editor-help.scss @@ -0,0 +1,29 @@ +.wysiwyg-help { + background: $whitish; + display: flex; + justify-content: space-between; + margin-top: -.5rem; + padding: .25rem .5rem; +} + +.drag-drop-help { + @extend %xsmall; + color: $gray; +} + +.help-markdown, +.help-button { + @extend %xsmall; + &:hover { + span { + transition: color .2s linear; + } + .icon { + color: $primary-light; + transition: color .2s linear; + } + } + .icon { + margin-right: .2rem; + } +} diff --git a/app/styles/components/estimation.scss b/app/styles/components/estimation.scss index 3f50666d..39a0f6e3 100644 --- a/app/styles/components/estimation.scss +++ b/app/styles/components/estimation.scss @@ -1,48 +1,57 @@ -%estimation { +@mixin estimation { .points-per-role { display: flex; - flex-wrap: wrap; - justify-content: center; + flex-direction: column; } .ticket-role-points { - background: rgba($gray-light, .1); - border-radius: 2px; - color: rgba($grayer, .3); - flex-basis: 20%; - flex-grow: 1; - flex-shrink: 0; - margin: .1rem; - padding: .5rem 0 .1rem; + background: rgba($gray-light, .2); + color: $gray-light; + display: flex; + flex: 1; + justify-content: space-between; + margin-bottom: .1rem; + min-height: 2rem; + padding: .5rem 1rem; position: relative; - text-align: center; transition: color .3s linear; - &.active { - background: rgba($primary-light, .9); - color: $whitish; - } - &:first-child { - background: rgba($grayer, .25); - border-radius: 0; - color: $whitish; + &.clickable { + &:hover, + &.active { + background: rgba($primary-light, .9); + color: $whitish; + .points, + .role, + .icon-arrow-bottom { + color: $whitish; + } + } } &:last-child { - border: 0; + background: rgba($primary-dark, .5); + border-bottom: 0; + color: $whitish; + .points, + .role, + .icon-arrow-bottom { + @extend %text; + color: $whitish; + } + } + .icon-arrow-bottom { + color: $gray-light; + margin-left: .25rem; + vertical-align: middle; } .points { - @extend %larger; - @extend %text; - display: block; - text-align: center; + @extend %light; + color: $grayer; } .role { - @extend %small; - @include ellipsis(90%); - display: inline-block; - text-align: center; + @extend %light; } } .popover { - @include popover(200px, $top: 105%, $left: 35%, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 10px, $arrow-height: 10px); + @include popover(200px, $top: 100%, $left: .5rem, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 10px, $arrow-height: 10px); li { display: inline-block; width: 23%; @@ -57,11 +66,11 @@ } } &.fix { - @include popover(200px, $top: 105%, $left: -160px, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 90%, $arrow-height: 10px); + @include popover(200px, $top: 100%, $left: -160px, $arrow-width: 10px, $arrow-top: -5px, $arrow-left: 90%, $arrow-height: 10px); } } } .ticket-estimation { - @extend %estimation; + @include estimation; } diff --git a/app/styles/components/list-items.scss b/app/styles/components/list-items.scss index b042c028..0b715bac 100644 --- a/app/styles/components/list-items.scss +++ b/app/styles/components/list-items.scss @@ -35,6 +35,17 @@ h2 { @extend %large; } + .list-itemtype-project-data-wrapper { + display: flex; + } + .list-itemtype-project-image { + flex-shrink: 0; + margin-right: .5rem; + width: 3rem; + img { + width: 100%; + } + } .list-itemtype-project-members { align-self: flex-end; display: flex; diff --git a/app/styles/components/markdown-help.scss b/app/styles/components/markdown-help.scss deleted file mode 100644 index f43ae37b..00000000 --- a/app/styles/components/markdown-help.scss +++ /dev/null @@ -1,19 +0,0 @@ -a.help-markdown, -a.help-button { - @extend %small; - color: $gray-light; - &:hover { - span { - color: $grayer; - transition: color .2s linear; - } - .icon { - color: $primary-light; - transition: color .2s linear; - } - } - .icon { - color: $gray-light; - margin-right: .2rem; - } -} diff --git a/app/styles/components/private.scss b/app/styles/components/private.scss index 00daaaad..943986c3 100644 --- a/app/styles/components/private.scss +++ b/app/styles/components/private.scss @@ -3,8 +3,7 @@ margin-left: .5rem; width: .5rem; svg { - height: .5rem; - width: .5rem; + @include svg-size(); } path { fill: $gray-light; diff --git a/app/styles/components/summary.scss b/app/styles/components/summary.scss index cd25dc2f..3c25c075 100644 --- a/app/styles/components/summary.scss +++ b/app/styles/components/summary.scss @@ -63,6 +63,8 @@ $summary-background: $grayer; svg { fill: $black; height: 100%; + max-height: 2rem; + max-width: 2rem; transition: all .2s; width: 100%; } diff --git a/app/styles/components/tag.scss b/app/styles/components/tag.scss index 7cf06594..235c061f 100644 --- a/app/styles/components/tag.scss +++ b/app/styles/components/tag.scss @@ -38,8 +38,12 @@ display: inline-block; } input { + margin-right: .25rem; padding: .4rem; width: 14rem; + +.icon-floppy { + margin-left: .5rem; + } } .tag { @extend %small; diff --git a/app/styles/components/user-list.scss b/app/styles/components/user-list.scss index 6247ddff..299014ae 100644 --- a/app/styles/components/user-list.scss +++ b/app/styles/components/user-list.scss @@ -1,11 +1,11 @@ -%user-list { +@mixin user-list { .user-list-single { align-content: center; align-items: center; background: transparent; border-bottom: 1px solid $whitish; display: flex; - padding: .5rem .5rem .3rem; + padding: .25rem 0; vertical-align: middle; &:last-child { @@ -28,7 +28,7 @@ } .assigned-to-list { - @extend %user-list; + @include user-list; margin-top: 1rem; .user-list-single { &:hover, @@ -59,7 +59,7 @@ } .ticket-watchers { - @extend %user-list; + @include user-list; margin-top: 1rem; .user-list-single { &:hover { diff --git a/app/styles/core/elements.scss b/app/styles/core/elements.scss index 973d8346..cfd2bbf5 100644 --- a/app/styles/core/elements.scss +++ b/app/styles/core/elements.scss @@ -47,6 +47,11 @@ sup { cursor: move; } +svg { + height: 1rem; + width: 1rem; +} + //Datepicker .pika-single { // scss-lint:disable ImportantRule @@ -79,3 +84,11 @@ sup { } } } + +.spin { + img { + @extend %loading-spinner; + max-height: 2rem; + max-width: 2rem; + } +} diff --git a/app/styles/core/forms.scss b/app/styles/core/forms.scss index ea30f641..1ef309d3 100644 --- a/app/styles/core/forms.scss +++ b/app/styles/core/forms.scss @@ -21,6 +21,14 @@ textarea { transition: border .3s linear; } } +button, +button:active, +button:focus { + border: 0; + outline: 0; + outline-style: none; + outline-width: 0; +} textarea { min-height: 10rem; resize: vertical; diff --git a/app/styles/core/typography.scss b/app/styles/core/typography.scss index 2f5cf9df..f353c83c 100755 --- a/app/styles/core/typography.scss +++ b/app/styles/core/typography.scss @@ -5,14 +5,21 @@ OpenSans-CondLight, OpenSans-Light, OpenSans-Regular, -OpenSans-Semibold, -taiga { +OpenSans-Semibold { @font-face { font-family: '#{$font-face}'; src: url('../fonts/#{$font-face}.ttf') format('truetype'); } } +@font-face { + font-family: taiga; font-style: normal; font-weight: normal; + src: url('../fonts/taiga.eot'); + src: url('../fonts/taiga.eot?') format('eot'), + url('../fonts/taiga.woff') format('woff'), + url('../fonts/taiga.ttf') format('truetype'); +} + h1, h2, h3, @@ -29,14 +36,14 @@ h6 { } h1 { - @extend %xxlarge; - @extend %title; + @extend %xlarge; + @extend %light; line-height: 1.5; margin-bottom: 1rem; text-transform: uppercase; span { - @extend %xxlarge; + @extend %larger; margin-right: .5rem; overflow: hidden; text-overflow: ellipsis; @@ -70,8 +77,8 @@ h1 { } h2 { - @extend %xlarge; - @extend %title; + @extend %larger; + @extend %text; line-height: 1.2; margin-bottom: 1rem; } diff --git a/app/styles/dependencies/mixins.scss b/app/styles/dependencies/mixins.scss index acb113d4..b9101fad 100644 --- a/app/styles/dependencies/mixins.scss +++ b/app/styles/dependencies/mixins.scss @@ -109,7 +109,6 @@ @extend %small; color: $gray-light; display: flex; - flex-basis: 150px; flex-shrink: 0; justify-content: flex-end; .list-itemtype-track-likers { @@ -133,11 +132,37 @@ } .icon { svg { - max-height: 1rem; - max-width: 1rem; + height: 1rem; + width: 1rem; } path { fill: currentcolor; } } } + +@mixin cursor-progress { + .in-progress { + cursor: progress; + } +} + +@mixin centered { + margin: 1rem auto; + max-width: 1200px; + min-width: 768px; + @include breakpoint(tablet) { + width: 90%; + min-width: 0; + } +} + +@mixin svg-size($width: 1rem, $height: null) { + @if $height == null { + width: $width; + height: $width; + } @else { + width: $width; + height: $height; + } +} diff --git a/app/styles/dependencies/mixins/profile-form.scss b/app/styles/dependencies/mixins/profile-form.scss new file mode 100644 index 00000000..afdbbe2b --- /dev/null +++ b/app/styles/dependencies/mixins/profile-form.scss @@ -0,0 +1,73 @@ +@mixin profile-form { + form { + display: flex; + } + fieldset { + margin-bottom: 1rem; + } + label { + @extend %light; + display: block; + margin-bottom: .2rem; + } + .project-details-image { + flex-shrink: 0; + flex-grow: 0; + width: 180px; + margin-right: 2rem; + .image { + width: 100%; + } + .loading-spinner { + + } + } + + .image-container { + position: relative; + margin-bottom: 0; + } + .loading-overlay { + display: none; + &.active { + align-items: center; + background: rgba($blackish, .8); + bottom: 0; + display: flex; + left: 0; + position: absolute; + right: 0; + top: 0; + width: 100%; + } + } + .loading-spinner { + @extend %loading-spinner; + border: 0; + transform-origin: center center; + } + .image-help { + @extend %xsmall; + line-height: 1rem; + margin-bottom: .5rem; + text-align: center; + } + .use-default-image { + @extend %xsmall; + text-align: center; + &:hover { + color: $red; + } + } + + .project-details-form-data { + flex: 1; + max-width: 500px; + } + + @include breakpoint(tablet) { + form { + display: block; + } + } +} diff --git a/app/styles/dependencies/mixins/project-card.scss b/app/styles/dependencies/mixins/project-card.scss new file mode 100644 index 00000000..ae606573 --- /dev/null +++ b/app/styles/dependencies/mixins/project-card.scss @@ -0,0 +1,79 @@ +@mixin project-card { + background: $white; + border: 1px solid $whitish; + margin: .5rem; + .tags-container { + display: flex; + height: .3rem; + } + .project-tag { + flex: 1; + } + .project-card-inner { + padding: 1rem; + display: flex; + flex-direction: column; + flex-wrap: wrap; + width: 100%; + } + .project-card-description { + @extend %small; + @extend %light; + color: $gray; + } + .project-card-statistics { + display: flex; + margin-top: auto; + svg { + @include svg-size(.8rem); + fill: $gray-light; + } + .svg-eye-closed { + display: none; + } + } + .statistic { + @extend %small; + color: $gray-light; + display: inline-block; + margin-right: .5rem; + &.active { + color: $primary; + svg { + fill: $primary; + } + } + } + .project-card-header { + display: flex; + flex-grow: 0; + } + .project-card-logo { + display: inline-block; + flex-basis: 50px; + width: 50px; + height: 50px; + margin-right: .5rem; + img { + width: 100%; + } + } + .project-card-name { + line-height: .9; + a { + @extend %large; + @extend %large; + color: $primary; + &:hover { + color: $primary-light; + } + } + } + .look-for-people { + svg { + @include svg-size(1rem); + fill: $gray-light; + margin: 0 .5rem; + } + } +} diff --git a/app/styles/layout/auth.scss b/app/styles/layout/auth.scss index 393e1eb6..1898df12 100644 --- a/app/styles/layout/auth.scss +++ b/app/styles/layout/auth.scss @@ -14,10 +14,12 @@ flex-basis: 400px; } .logo-svg { - max-height: 140px; - padding: 0 33%; text-align: center; width: 100%; + svg { + height: 8rem; + width: 8rem; + } } .logo { @extend %xxlarge; diff --git a/app/styles/layout/backlog.scss b/app/styles/layout/backlog.scss index b5edef9e..77b905f8 100644 --- a/app/styles/layout/backlog.scss +++ b/app/styles/layout/backlog.scss @@ -19,7 +19,7 @@ background: lighten($gray, 30%); } } - &.move-to-current-sprint { + &.move-to-sprint { display: none; } } diff --git a/app/styles/layout/ticket-detail.scss b/app/styles/layout/ticket-detail.scss index 3fa22c44..a2d5bedd 100644 --- a/app/styles/layout/ticket-detail.scss +++ b/app/styles/layout/ticket-detail.scss @@ -1,5 +1,5 @@ .us-story-main-data { - margin-bottom: 2rem; + margin-bottom: 1rem; header { align-content: center; align-items: stretch; @@ -10,9 +10,11 @@ .us-title { @extend %large; @extend %text; + align-items: center; background: $whitish; + display: flex; flex: 1; - padding: 1rem; + padding: .5rem; position: relative; transition: all .2s linear; &.blocked { @@ -64,11 +66,14 @@ flex-grow: 1; } .us-title-text { + @extend %larger; + @extend %text; align-content: center; align-items: center; display: flex; + flex: 1; margin-bottom: 0; - max-width: 94%; + max-width: 92%; } .us-title-text:hover { .icon-edit { @@ -77,16 +82,14 @@ } } .us-number { - @extend %xlarge; - @extend %title; + @extend %text; color: $gray-light; flex-shrink: 0; line-height: 2.2rem; margin-right: .5rem; } .us-name { - @extend %xlarge; - color: $grayer; + color: $gray; display: inline-block; line-height: 2.2rem; padding-right: 1rem; @@ -165,7 +168,32 @@ } } +.subheader { + display: flex; + justify-content: space-between; + @include breakpoint(laptop) { + flex-direction: column; + justify-content: flex-start; + } + .ticket-created-by { + flex: 1; + flex-basis: 250px; + @include breakpoint(laptop) { + flex: 0; + order: 1; + } + } + .tags-block { + flex: 3; + @include breakpoint(laptop) { + flex: 0; + order: 2; + } + } +} + .duty-content { + @include cursor-progress; position: relative; &:hover { .view-description { diff --git a/app/styles/layout/wiki.scss b/app/styles/layout/wiki.scss index 1285b1d9..50134ab9 100644 --- a/app/styles/layout/wiki.scss +++ b/app/styles/layout/wiki.scss @@ -24,6 +24,7 @@ } .wiki-content { + @include cursor-progress; margin-bottom: 2rem; position: relative; &.editable { diff --git a/app/styles/modules/admin/admin-project-profile.scss b/app/styles/modules/admin/admin-project-profile.scss index 044cdb76..5e4e0483 100644 --- a/app/styles/modules/admin/admin-project-profile.scss +++ b/app/styles/modules/admin/admin-project-profile.scss @@ -1,74 +1,106 @@ +@import '../dependencies/mixins/profile-form'; + .project-details { - form { - max-width: 700px; - width: 100%; + @include profile-form; + .looking-for-people { + @extend %light; + border-bottom: 1px solid $whitish; + border-top: 1px solid $whitish; + padding: 1rem 0; } - input, - textarea { - @extend %title; + .looking-for-people-selector { + align-items: center; + display: flex; + svg { + @include svg-size(); + fill: $gray-light; + margin-left: .5rem; + } + .check { + margin-left: auto; + } } - fieldset { - margin-bottom: 1rem; + .looking-for-people-reason { + display: block; + margin-top: 1rem; + &.ng-hide-remove-active { + animation: dropdownFade .3s; + } + &.ng-hide-add-active { + animation: dropdownFade .2s reverse; + animation-delay: .1s; + } + } + + .delete-project { + @extend %xsmall; + display: block; + margin-top: 1rem; + text-align: right; + &:hover { + color: $red; + } + } + .private-or-public { + @extend %xsmall; + color: $gray-light; + margin-bottom: 2rem; + svg { + @include svg-size(1.1rem); + fill: $gray-light; + margin-right: .5rem; + vertical-align: middle; + } + } + +} + +.project-privacy-settings { + display: flex; + margin-bottom: .5rem; + .privacy-option { + flex: 1; + transition: .2 linear; + &:first-child { + margin-right: .5rem; + } + } + input[type="radio"] { + display: none; + } + input[type="text"] { + display: none; } label { - @extend %title; - display: block; - margin-bottom: .2rem; - } - textarea { - height: 10rem; - } - .privacy-settings { - display: flex; - margin-bottom: 2rem; - > div { - flex-basis: 0; - flex-grow: 1; - overflow: hidden; - position: relative; - &:first-child { - margin-right: .5rem; + background: $whitish; + color: $grayer; + text-align: center; + transition: all .2s linear; + &:hover { + background: rgba($primary-light, .4); + color: $grayer; + svg { + fill: $grayer; } } - label { - @extend %title; - border: 1px solid $gray-light; - cursor: not-allowed; - display: block; - text-align: center; - transition: all .2s linear; - span { - color: $gray-light; - } + svg { + @include svg-size(1.1rem); + fill: $grayer; + margin-left: .5rem; + vertical-align: middle; } } - .privacy-project { - cursor: pointer; - height: 50px; - left: -10px; - opacity: 0; - position: absolute; - top: -10px; - width: 500px; - z-index: 999; - } .privacy-project:checked { + label { background: $primary-light; - border: 1px solid $primary-light; - span { - color: $white; + color: $white; + svg { + @include svg-size(1.1rem); + fill: $white; } } - } - .button-green { - color: $white; - display: block; - text-align: center; - } - .delete-project { - @extend %small; - display: block; - margin-top: 1rem; + ~input[type="text"] { + display: block; + } } } diff --git a/app/styles/modules/backlog/sprints.scss b/app/styles/modules/backlog/sprints.scss index 40e4a071..7ba4d2cb 100644 --- a/app/styles/modules/backlog/sprints.scss +++ b/app/styles/modules/backlog/sprints.scss @@ -17,6 +17,8 @@ } svg { height: 1.4rem; + max-height: 1.4rem; + max-width: 1.5rem; width: 1.5rem; } path { diff --git a/app/styles/modules/backlog/taskboard-table.scss b/app/styles/modules/backlog/taskboard-table.scss index 6b6b5983..dae0c4fb 100644 --- a/app/styles/modules/backlog/taskboard-table.scss +++ b/app/styles/modules/backlog/taskboard-table.scss @@ -68,9 +68,11 @@ $column-margin: 0 10px 0 0; position: absolute; } .task-colum-name { - @extend %large; + @extend %medium; + align-items: center; background: $whitish; border-top: 3px solid $gray-light; + color: $gray; display: flex; flex-basis: $column-width; flex-grow: $column-flex; diff --git a/app/styles/modules/common/assigned-to.scss b/app/styles/modules/common/assigned-to.scss index 207ae322..a30a5e48 100644 --- a/app/styles/modules/common/assigned-to.scss +++ b/app/styles/modules/common/assigned-to.scss @@ -1,7 +1,10 @@ .ticket-assigned-to { align-items: center; + border-bottom: 1px solid $gray-light; + border-top: 1px solid $gray-light; display: flex; margin-bottom: 1rem; + padding: 1rem 0; position: relative; &:hover { .assigned-to { @@ -18,11 +21,31 @@ max-width: 2rem; } .user-avatar { - flex-basis: 4rem; + flex-basis: 3rem; flex-shrink: 0; + position: relative; img { width: 100%; } + &.is-iocaine { + img { + filter: hue-rotate(150deg) saturate(200%); + } + } + .iocaine-symbol { + left: -.5rem; + position: absolute; + top: -.75rem; + z-index: 9; + svg { + background: $grayer; + border-radius: .25rem; + fill: $white; + min-height: 1.75rem; + min-width: 1.75rem; + padding: .25rem; + } + } } .assigned-to { flex-grow: 1; @@ -32,29 +55,31 @@ @extend %light; color: $gray; display: block; + margin: .2rem 0 .25rem; } - .user-assigned { - @extend %large; + .assigned-to-options { + display: inline-block; + } + .user-assigned, + .assign-to-me { color: $primary; cursor: default; - line-height: 1.5rem; - &.editable { + &:hover { cursor: pointer; } .icon { vertical-align: middle; } - } - .assigned-name { - @include ellipsis(80%); - display: inline-block; + > span { + @include ellipsis(80%); + } } .icon-delete { color: $gray-light; opacity: 0; position: absolute; right: 0; - top: 0; + top: 2rem; &:hover { color: $red; } diff --git a/app/styles/modules/common/attachments.scss b/app/styles/modules/common/attachments.scss deleted file mode 100644 index 48aa96a0..00000000 --- a/app/styles/modules/common/attachments.scss +++ /dev/null @@ -1,190 +0,0 @@ -.attachments { - margin-bottom: 2rem; -} - -.attachments-header { - align-content: space-between; - align-items: center; - background: $whitish; - display: flex; - justify-content: space-between; - padding: .5rem 1rem; - .attachments-title { - @extend %medium; - @extend %bold; - color: $grayer; - } - .attachments-num, - .attachments-text { - margin-right: .1rem; - } - .icon { - @extend %large; - color: $grayer; - cursor: pointer; - &:hover { - color: $primary; - transition: color .2s ease-in; - } - } -} - -.single-attachment { - @extend %small; - align-items: center; - border-bottom: 1px solid $whitish; - display: flex; - padding: .5rem 0 .5rem 1rem; - position: relative; - &:hover { - .attachment-settings { - .settings { - opacity: 1; - transition: opacity .2s ease-in; - } - } - } - &.ui-sortable-helper { - background: lighten($primary, 60%); - box-shadow: 1px 1px 10px rgba($black, .1); - transition: background .2s ease-in; - } - &.deprecated { - color: $gray-light; - .attachment-name a { - color: $gray-light; - } - } - &.sortable-placeholder { - background: $whitish; - height: 40px; - } - .attachment-name { - @extend %bold; - @include ellipsis(200px); - flex-basis: 35%; - flex-grow: 1; - padding-right: 1rem; - .icon { - margin-right: .5rem; - } - } - .attachment-size { - color: $gray-light; - flex-basis: 15%; - flex-grow: 1; - margin-right: .5rem; - } - .attachment-comments, - .editable-attachment-comment { - flex-basis: 35%; - flex-grow: 1; - span { - color: $gray; - } - } - .editable-attachment-comment { - @extend %small; - } - .attachment-settings { - flex-basis: 15%; - flex-grow: 1; - .settings, - .editable-settings { - @extend %large; - color: $gray-light; - display: block; - position: absolute; - &:hover { - color: $primary; - } - } - .settings { - opacity: 0; - top: .5rem; - } - .editable-settings { - opacity: 1; - top: 1rem; - } - .icon-edit, - .icon-floppy { - right: 3.5rem; - } - .icon-delete { - right: 2rem; - &:hover { - color: $red; - } - } - .icon-drag-v { - cursor: move; - right: 0; - } - } - .icon-delete { - @extend %large; - color: $gray-light; - &:hover { - color: $red; - } - } - .editable-attachment-deprecated { - padding-left: 1rem; - span { - color: $gray-light; - } - input { - margin-right: .2rem; - vertical-align: middle; - &:checked+span { - color: $grayer; - } - } - } - .percentage { - background: rgba($primary, .1); - bottom: 0; - height: 40px; - left: 0; - position: absolute; - top: 0; - width: 45%; - } -} - -.more-attachments { - @extend %small; - border-bottom: 1px solid $gray-light; - display: block; - padding: 1rem 0 1rem 1rem; - span { - color: $gray-light; - } - .more-attachments-num { - color: $primary; - margin-left: .5rem; - } - &:hover { - background: lighten($primary, 60%); - transition: background .2s ease-in; - } -} - -.add-attach { - cursor: pointer; - overflow: hidden; - position: relative; - input { - display: none; - } - span { - @extend %small; - color: $gray-light; - } -} - -.attachment-preview img { - max-height: 95vh; - max-width: 95vw; -} diff --git a/app/styles/modules/common/history.scss b/app/styles/modules/common/history.scss index 3c6947c3..7145097e 100644 --- a/app/styles/modules/common/history.scss +++ b/app/styles/modules/common/history.scss @@ -67,10 +67,12 @@ } } .add-comment { + @include cursor-progress; @include clearfix; &.active { .button-green { display: block; + margin-top: .5rem; } textarea { height: 6rem; @@ -89,7 +91,6 @@ textarea { background: $white; height: 5rem; - margin-bottom: .5rem; min-height: 41px; } .help-markdown { diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 14c3c70a..7f57453c 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -18,12 +18,9 @@ } textarea { - margin-bottom: 1rem; - max-height: 9rem; - min-height: 7rem; + min-height: 4.5rem; resize: vertical; } - label { @extend %xsmall; background: $whitish; @@ -47,12 +44,12 @@ .settings { display: flex; justify-content: center; - margin-bottom: 1rem; fieldset { margin-right: .5rem; &:hover { color: $white; transition: all .2s ease-in; + transition-delay: .2s; } &:last-child { margin: 0; @@ -98,6 +95,26 @@ display: none; } } + .attachments { + margin-bottom: 0; + } + .attachment-body { + max-height: 7.5rem; + overflow-y: auto; + } + .attachment-delete { + right: .5rem; + svg { + fill: $gray-light; + height: 1.25rem; + width: 1.25rem; + } + &:hover { + svg { + fill: $red; + } + } + } } .lightbox-generic-bulk { @@ -248,6 +265,7 @@ .subtitle { @extend %large; @extend %title; + white-space: pre-line; } .options { display: flex; @@ -490,18 +508,23 @@ } .lb-create-edit-userstory { + .points-per-role { + flex-direction: row; + flex-wrap: wrap; + margin: 0; + } .ticket-role-points { - flex-grow: 1; - flex-shrink: 1; - max-width: calc(100% * (1/5) - .2rem); + margin: .1rem; + min-width: 20%; &:first-child { margin-left: 0; } - &:nth-child(5n+5) { + &:nth-child(4n + 4) { margin-right: 0; } - } - .points-per-role { - margin-bottom: 1rem; + &:last-child { + margin: .1rem 0; + min-width: 100%; + } } } diff --git a/app/styles/modules/common/nav.scss b/app/styles/modules/common/nav.scss index 6dca70cf..4472d51a 100644 --- a/app/styles/modules/common/nav.scss +++ b/app/styles/modules/common/nav.scss @@ -3,8 +3,17 @@ $label-arrow-wh: 12px; tg-project-menu { background-position: 0 -300px; min-height: $main-height; + min-width: 50px; padding: 1rem 0; + position: relative; text-transform: uppercase; + z-index: 9; + .menu { + &.menu-fixed { + position: fixed; + top: 1rem; + } + } } .main-nav { @@ -49,8 +58,12 @@ tg-project-menu { } } } - svg path { - opacity: 1; + svg { + height: 1.5rem; + width: 1.5rem; + path { + opacity: 1; + } } span { display: block; diff --git a/app/styles/modules/common/related-tasks.scss b/app/styles/modules/common/related-tasks.scss index 9655cee8..26867eec 100644 --- a/app/styles/modules/common/related-tasks.scss +++ b/app/styles/modules/common/related-tasks.scss @@ -9,18 +9,27 @@ background: $whitish; display: flex; justify-content: space-between; - padding: .5rem 1rem; + min-height: 36px; .related-tasks-title { @extend %medium; @extend %bold; + margin-left: 1rem; } - .icon { - @extend %large; - color: $grayer; - cursor: pointer; - &:hover { - color: $primary; - transition: color .2s ease-in; + .add-button { + background: $grayer; + border: 0; + display: inline-block; + padding: .5rem; + transition: background .25s; + &:hover, + &.is-active { + background: $primary-light; + } + svg { + fill: $white; + height: 1.25rem; + margin-bottom: -.2rem; + width: 1.25rem; } } } diff --git a/app/styles/modules/common/ticket-data.scss b/app/styles/modules/common/ticket-data.scss index a727f403..c97d7e36 100644 --- a/app/styles/modules/common/ticket-data.scss +++ b/app/styles/modules/common/ticket-data.scss @@ -1,24 +1,57 @@ -.ticket-data { +.ticket-header { + align-items: center; + display: flex; + margin: 1.5rem 0 2rem; + position: relative; .ticket-title { @extend %larger; @extend %light; - margin: 1.5rem 0 2rem; text-transform: uppercase; - span { - margin-right: .25rem; - &:last-child { - @extend %large; - } - } + vertical-align: sub; } - .ticket-data-container { + .detail-status { @extend %small; - @extend %normal; - margin-bottom: 1rem; - .icon { + display: inline-block; + margin-left: .25rem; + position: relative; + .icon-arrow-bottom { margin-left: .25rem; } } + .detail-status-inner { + color: $white; + display: flex; + justify-content: center; + padding: .15rem .25rem; + text-transform: uppercase; + } + .pop-status { + @include popover(150px, 1.25rem, 0, '', ''); + padding: 0; + text-transform: none; + a { + @extend %text; + padding: .5rem 1rem; + } + a:hover { + background: rgba($primary-light, .2); + } + } +} + +.ticket-data-container { + @extend %small; + @extend %normal; + margin-bottom: 1rem; + .icon { + margin-left: .25rem; + } +} + +.ticket-status { + &:last-child { + margin: 0; + } .level { display: inline-block; margin-right: .5rem; @@ -29,29 +62,6 @@ float: right; text-transform: lowercase; } - - .ticket-estimation { - .ticket-role-points { - max-width: 25%; - &:first-child { - flex-basis: 100%; - max-width: 100%; - } - } - } -} - -.ticket-status { - margin-bottom: .5rem; - &:last-child { - margin: 0; - } - div { - background: darken($whitish, 5%); - padding: .5rem; - padding-right: 1rem; - transition: background .2s ease-in; - } .clickable { &:hover { background: darken($whitish, 10%); @@ -59,107 +69,115 @@ } } .type-data { + background: darken($whitish, 5%); + margin-bottom: .5rem; + padding: .5rem; + padding-right: 1rem; position: relative; + transition: background .2s ease-in; .pop-type { @include popover(150px, '', 30px, '', ''); } } .severity-data { + background: darken($whitish, 5%); + margin-bottom: .5rem; + padding: .5rem; + padding-right: 1rem; position: relative; + transition: background .2s ease-in; .pop-severity { @include popover(150px, '', 30px, '', ''); } } .priority-data { + background: darken($whitish, 5%); + margin-bottom: .5rem; + padding: .5rem; + padding-right: 1rem; position: relative; + transition: background .2s ease-in; .pop-priority { @include popover(150px, '', 30px, '', ''); } } - .status-data { - position: relative; - .pop-status { - @include popover(150px, '', 30px, '', ''); - } - } } -.ticket-track-buttons { - .track-inner { +.ticket-watch-buttons { + margin-bottom: 1rem; +} + +.ticket-watch { + .ticket-watch-title { + @extend %bold; + margin-bottom: .5rem; + } + .ticket-watch-inner { + display: flex; + } + .track-icon { + position: relative; + top: 2px; + } + .ticket-watch-button, + .add-watcher { @extend %light; @extend %small; - background: darken($whitish, 5%); + background: $gray-light; + color: $white; + flex: 1; padding: .25rem; + text-align: center; text-transform: uppercase; transition: background .25s; - &:hover { - background: darken($whitish, 10%); - } - } - .track-button { - width: 100%; - } - .active { - .track-button-counter { - background: rgba($grayer, .5); + path { + fill: $white; } &:hover { - .track-inner { - background: rgba($primary-light, .2); - } + background: $primary-light; } &.is-hover { - .track-inner { - background: $red; - color: $whitish; - transition: background .3s; - } - path { + background: $red; + color: $whitish; + transition: background .3s; + svg { fill: $red-light; } } - } - .track-button-counter { - @extend %large; - background: rgba($grayer, .25); - color: $whitish; - padding: 0 .5rem; - } - .vote-button { - margin-bottom: .3rem; - } - .watch-button { - border-bottom: 0; - } - .ticket-watchers { - margin: .5rem 0; - } - .add-watcher { - display: block; - margin: .5rem; - .icon { - background: rgba($grayer, .25); - color: $whitish; - margin-right: .5rem; - padding: .25rem; - } - &:hover { - .icon { - background: $primary-light; - color: $whitish; - transition: background .3s linear; + &.active { + background: $primary-light; + &:hover { + background: $red-light; + } + svg { + fill: $white; } } + + } + .ticket-watch-button { + margin-right: .25rem; } } .ticket-detail-settings { + display: flex; + justify-content: center; margin-top: 2rem; label, - .button { - display: block; - margin-bottom: .5rem; - text-align: center; + .item-block, + .item-unblock, + .promote-button, + .button-delete { + background: $gray-light; + display: inline-block; + margin-right: .5rem; + padding: 1rem; + transition: background .2s linear; + transition-delay: .1s; + &:hover { + background: $gray; + } &.editable { cursor: pointer; } @@ -167,34 +185,35 @@ display: none; } } - .loading-spinner { - @extend %loading-spinner; + .item-block, + .item-unblock { + display: none; + &.is-active { + display: inline-block; + } } - .button-gray { - background: $gray-light; + .item-unblock { + background: $red-light; &:hover { - background: $gray-light; - } - &.editable { - &:hover { - background: $grayer; - cursor: pointer; - } - } - &.active { - background: $primary; + background: $red; } } - .item-block { - &.editable { - &:hover { - background: $red; - cursor: pointer; - } + .button-delete { + background: $red-light; + &:hover { + background: $red; } } - .button-red { - display: block; - margin-top: 2rem; + img { + max-height: 1.25rem; + max-width: 1.25rem; + width: 100%; + } + svg { + fill: $white; + height: 100%; + max-height: 1.25rem; + max-width: 1.25rem; + width: 100%; } } diff --git a/app/styles/modules/home-project.scss b/app/styles/modules/home-project.scss index 187d441c..a58aab65 100644 --- a/app/styles/modules/home-project.scss +++ b/app/styles/modules/home-project.scss @@ -1,24 +1,35 @@ .single-project { .single-project-intro { + display: flex; margin-bottom: 2rem; } + .project-logo { + margin-right: 1rem; + width: 6rem; + img { + width: 100%; + } + } + .single-project-title-wrapper { + flex: 1; + } .intro-options { align-items: center; display: flex; justify-content: space-between; + margin-bottom: .5rem; + } + .intro-title { + align-items: center; + display: flex; } h1 { color: $primary; display: inline-block; line-height: 1.2; margin-bottom: 0; - margin-right: 3rem; vertical-align: middle; } - .private { - font-size: 1rem; - vertical-align: super; - } .like-watch-container { margin-left: auto; } @@ -34,6 +45,7 @@ .description { @extend %light; @extend %medium; + margin: 0; } .project-data { display: flex; @@ -60,6 +72,18 @@ max-width: 960px; width: 0; } + .looking-for-people { + img { + width: 100%; + } + h3 { + @extend %small; + } + p { + @extend %small; + @extend %light; + } + } .involved-data { flex-basis: 220px; width: 220px; @@ -70,8 +94,8 @@ flex-wrap: wrap; margin-bottom: 1rem; li { + flex-basis: 24%; margin-right: .14rem; - width: 24%; &:nth-child(4n) { margin-right: 0; } diff --git a/app/styles/modules/kanban/kanban-table.scss b/app/styles/modules/kanban/kanban-table.scss index 544e08f1..817c8a58 100644 --- a/app/styles/modules/kanban/kanban-table.scss +++ b/app/styles/modules/kanban/kanban-table.scss @@ -65,9 +65,11 @@ $column-margin: 0 10px 0 0; position: absolute; } .task-colum-name { - @extend %large; + @extend %medium; + align-items: center; background: $whitish; border-top: 3px solid $gray-light; + color: $gray; display: flex; flex-basis: $column-width; flex-grow: $column-flex; diff --git a/app/styles/modules/search/search-filter.scss b/app/styles/modules/search/search-filter.scss index 55b88173..742bd0b7 100644 --- a/app/styles/modules/search/search-filter.scss +++ b/app/styles/modules/search/search-filter.scss @@ -1,26 +1,33 @@ .search-filter { - ul { - border-bottom: 3px solid $gray-light; - display: flex; - padding-bottom: .5rem; - } - li { - margin-right: 1rem; - } + border-top: 1px solid $whitish; + display: flex; + margin-bottom: 0; + z-index: 9; a { - @extend %large; - @extend %title; - opacity: .2; + background: $white; + color: $gray-light; + display: inline-block; + margin-right: 1rem; + padding: 1rem 1.25rem; &:hover { - color: $gray; - opacity: 1; - transition: opacity .3s linear; + transition: color .3s linear; + .icon { + margin-right: .4rem; + } + .name { + padding-left: 5px; + } + } + &.active { + border-left: 1px solid $whitish; + border-right: 1px solid $whitish; + color: $grayer; + position: relative; + top: 1px; + .icon { + color: $primary-light; + } } - } - .active { - color: $gray; - opacity: 1; - transition: opacity .3s linear; } .icon { margin-right: .4rem; diff --git a/app/styles/modules/search/search-result-table.scss b/app/styles/modules/search/search-result-table.scss index 65371e7a..e970dfdf 100644 --- a/app/styles/modules/search/search-result-table.scss +++ b/app/styles/modules/search/search-result-table.scss @@ -1,14 +1,11 @@ .search-result-table { + border-top: 1px solid $whitish; .row { - align-content: center; align-items: center; display: flex; justify-content: space-between; padding: .5rem; - &:hover { - background: lighten($primary, 60%); - transition: background .2s ease-in; - } + .ref { flex-basis: 30px; flex-grow: 1; @@ -83,6 +80,10 @@ } } +.search-result-table-header { + @extend %bold; +} + .empty-search-results { margin-top: 4rem; text-align: center; diff --git a/app/styles/modules/user-settings/user-profile.scss b/app/styles/modules/user-settings/user-profile.scss index 1c10d313..16f492d1 100644 --- a/app/styles/modules/user-settings/user-profile.scss +++ b/app/styles/modules/user-settings/user-profile.scss @@ -1,81 +1,10 @@ +@import '../dependencies/mixins/profile-form'; + .user-profile { - form { - max-width: 700px; + @include profile-form; + max-width: 780px; + .submit-button { width: 100%; - .container { - display: flex; - } - .avatar-container { - flex-basis: 0; - flex-grow: 1; - margin-right: 1rem; - .image-container { - position: relative; - } - .avatar { - border-radius: 8%; - width: 100%; - } - .overlay { - align-items: center; - background: rgba($blackish, .8); - bottom: 0; - display: flex; - left: 0; - position: absolute; - right: 0; - top: 0; - width: 100%; - } - .loading-spinner { - @extend %loading-spinner; - border: 0; - min-height: 3rem; - min-width: 3rem; - transform-origin: center center; - } - p { - @extend %xsmall; - line-height: .8rem; - margin-bottom: .3rem; - text-align: center; - } - span { - @extend %bold; - } - .use-gravatar { - @extend %small; - cursor: pointer; - display: inline-block; - text-align: center; - width: 100%; - } - } - .data { - flex-basis: 0; - flex-grow: 3; - } - } - fieldset { - margin-bottom: 1rem; - } - .submit { - margin-top: 2rem; - } - label { - @extend %title; - display: block; - margin-bottom: .5rem; - } - textarea { - min-height: 7rem; - } - .button-green { - color: $white; - cursor: pointer; - display: block; - padding: 12px; - text-align: center; } .delete-account { @extend %small; diff --git a/app/svg/activity.svg b/app/svg/activity.svg new file mode 100644 index 00000000..66e8684f --- /dev/null +++ b/app/svg/activity.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/svg/attachment.svg b/app/svg/attachment.svg new file mode 100644 index 00000000..784be839 --- /dev/null +++ b/app/svg/attachment.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/client-requirement.svg b/app/svg/client-requirement.svg new file mode 100644 index 00000000..fd20989a --- /dev/null +++ b/app/svg/client-requirement.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/svg/discover.svg b/app/svg/discover.svg new file mode 100644 index 00000000..5334c1bb --- /dev/null +++ b/app/svg/discover.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/gallery.svg b/app/svg/gallery.svg new file mode 100644 index 00000000..d9f205bc --- /dev/null +++ b/app/svg/gallery.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/help.svg b/app/svg/help.svg new file mode 100644 index 00000000..b822b8fd --- /dev/null +++ b/app/svg/help.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/svg/iocaine.svg b/app/svg/iocaine.svg new file mode 100644 index 00000000..868c76d0 --- /dev/null +++ b/app/svg/iocaine.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/svg/list.svg b/app/svg/list.svg new file mode 100644 index 00000000..f2ef5c24 --- /dev/null +++ b/app/svg/list.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/lock.svg b/app/svg/lock.svg index b2ee1e5a..d39b2ba0 100644 --- a/app/svg/lock.svg +++ b/app/svg/lock.svg @@ -1,7 +1,3 @@ - - - - - - + + diff --git a/app/svg/pattern.svg b/app/svg/pattern.svg new file mode 100644 index 00000000..beb95ba7 --- /dev/null +++ b/app/svg/pattern.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/svg/project.svg b/app/svg/project.svg new file mode 100644 index 00000000..6e3ce332 --- /dev/null +++ b/app/svg/project.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/promote.svg b/app/svg/promote.svg new file mode 100644 index 00000000..329e718f --- /dev/null +++ b/app/svg/promote.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/recruit.svg b/app/svg/recruit.svg new file mode 100644 index 00000000..fa8dcf32 --- /dev/null +++ b/app/svg/recruit.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/svg/search.svg b/app/svg/search.svg new file mode 100644 index 00000000..8c58b6ec --- /dev/null +++ b/app/svg/search.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/svg/team-requirement.svg b/app/svg/team-requirement.svg new file mode 100644 index 00000000..154ea48c --- /dev/null +++ b/app/svg/team-requirement.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/svg/team.svg b/app/svg/team.svg new file mode 100644 index 00000000..fc0fc652 --- /dev/null +++ b/app/svg/team.svg @@ -0,0 +1,4 @@ + + + diff --git a/app/svg/trash.svg b/app/svg/trash.svg new file mode 100644 index 00000000..281f1d29 --- /dev/null +++ b/app/svg/trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/svg/unlock.svg b/app/svg/unlock.svg new file mode 100644 index 00000000..fe3188a5 --- /dev/null +++ b/app/svg/unlock.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/themes/high-contrast/custom.scss b/app/themes/high-contrast/custom.scss index a133091b..0ad07c93 100644 --- a/app/themes/high-contrast/custom.scss +++ b/app/themes/high-contrast/custom.scss @@ -358,3 +358,10 @@ tg-project-menu { } } + +// DASHBOARD +.home-wrapper { + .title-bar { + color: $white; + } +} diff --git a/app/themes/high-contrast/variables.scss b/app/themes/high-contrast/variables.scss index 80880d57..b2ee93d6 100755 --- a/app/themes/high-contrast/variables.scss +++ b/app/themes/high-contrast/variables.scss @@ -7,8 +7,8 @@ $black: #000; $blackish: #212121; $grayer: #212121; $gray: #757575; -$gray-light: #f5f5f5; -$whitish: #f5f5f5; +$gray-light: #757575; +$whitish: #757575; $white: #fff; // Primary colors @@ -59,7 +59,7 @@ $dropdown-color: rgba(darken($primary-dark, 20%), 1); %mono {font-family: 'courier new', 'monospace';} %lightbox { - background: rgba($white, .95); + background: rgba($white, 1); } // Background images diff --git a/app/themes/material-design/variables.scss b/app/themes/material-design/variables.scss index 6339c00e..a191c209 100755 --- a/app/themes/material-design/variables.scss +++ b/app/themes/material-design/variables.scss @@ -7,8 +7,8 @@ $black: #000; $blackish: #212121; $grayer: #424242; $gray: #757575; -$gray-light: #bdbdbd; -$whitish: #f5f5f5; +$gray-light: #BDBDBD; +$whitish: #EEEEEE; $white: #fff; // Primary colors @@ -56,7 +56,7 @@ $dropdown-color: rgba(darken($primary-dark, 20%), 1); // lightbox %lightbox { - background: rgba($white, .95); + background: rgba($white, .98); } // Background images diff --git a/app/themes/taiga/variables.scss b/app/themes/taiga/variables.scss index 12202bf2..4de07219 100755 --- a/app/themes/taiga/variables.scss +++ b/app/themes/taiga/variables.scss @@ -34,7 +34,6 @@ $yellow-pear: #bbe831; $tribe-primary: #98e0eb; $tribe-secondary: #107a8a; - $top-icon-color: #11241f; $dropdown-color: rgba(darken($grayer, 20%), 1); @@ -62,7 +61,7 @@ $dropdown-color: rgba(darken($grayer, 20%), 1); // lightbox %lightbox { - background: rgba($white, .95); + background: rgba($white, .98); } // Background images diff --git a/bower.json b/bower.json index 55f63b40..1846ba06 100644 --- a/bower.json +++ b/bower.json @@ -5,7 +5,7 @@ "authors": [ { "name": "Andrey Antukh", - "email": "niwi@niwi.be" + "email": "niwi@niwi.nz" }, { "name": "Jesus Espino Garcia", @@ -81,7 +81,7 @@ "angular-translate-interpolation-messageformat": "~2.8.1", "ngInfiniteScroll": "1.2.1", "eventemitter2": "~0.4.14", - "immutable": "~3.7.2", + "immutable": "~3.7.6", "bluebird": "~2.10.2", "intro.js": "~1.1.1" }, diff --git a/conf.e2e.js b/conf.e2e.js index e0b11b37..bca55590 100644 --- a/conf.e2e.js +++ b/conf.e2e.js @@ -40,7 +40,8 @@ exports.config = { kanban: "e2e/suites/kanban.e2e.js", projectHome: "e2e/suites/project-home.e2e.js", search: "e2e/suites/search.e2e.js", - team: "e2e/suites/team.e2e.js" + team: "e2e/suites/team.e2e.js", + discover: "e2e/suites/discover/*.e2e.js" }, onPrepare: function() { // track mouse movements diff --git a/e2e/helpers/admin-attributes-helper.js b/e2e/helpers/admin-attributes-helper.js index d9d0d53a..ad02791b 100644 --- a/e2e/helpers/admin-attributes-helper.js +++ b/e2e/helpers/admin-attributes-helper.js @@ -100,3 +100,9 @@ helper.getGenericForm = function(form) { helper.getGenericNames = function(section) { return section.$$('.status-name span').getText(); }; + +helper.waitLoad = function() { + return browser.wait(function() { + return $('.admin-attributes').isPresent(); + }, 5000); +}; diff --git a/e2e/helpers/backlog-helper.js b/e2e/helpers/backlog-helper.js index 4311a81d..b6021bc2 100644 --- a/e2e/helpers/backlog-helper.js +++ b/e2e/helpers/backlog-helper.js @@ -40,9 +40,7 @@ helper.getCreateEditUsLightbox = function() { return utils.popover.open(role, value); }, getRolePoints: function() { - let role = obj.roles().get(0); - - return role.$('.points').getText(); + return el.$$('.ticket-role-points').last().$('.points').getText(); } }; @@ -154,6 +152,10 @@ helper.setUsPoints = async function(item, value1, value2) { return utils.popover.open(points, value1, value2); }; +helper.getUsPoints = async function(item) { + return $$('.backlog-table-body > div .us-points').get(item).$$('span').get(0).getText(); +}; + helper.deleteUs = function(item) { $$('.backlog-table-body > div .icon-delete').get(item).click(); }; diff --git a/e2e/helpers/common-helper.js b/e2e/helpers/common-helper.js index 6bd7ee3e..df99bd9f 100644 --- a/e2e/helpers/common-helper.js +++ b/e2e/helpers/common-helper.js @@ -1,6 +1,12 @@ var utils = require('../utils'); var helper = module.exports; +var chai = require('chai'); +var chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +var expect = chai.expect; + helper.assignToLightbox = function() { let el = $('div[tg-lb-assignedto]'); @@ -37,3 +43,23 @@ helper.assignToLightbox = function() { return obj; }; + +helper.lightboxAttachment = async function() { + let el = $('tg-attachments-simple'); + + let addAttachment = el.$('#add-attach'); + + let countAttachments = await el.$$('.single-attachment').count(); + + var fileToUpload1 = utils.common.uploadImagePath(); + var fileToUpload2 = utils.common.uploadFilePath(); + + await utils.common.uploadFile(addAttachment, fileToUpload1); + await utils.common.uploadFile(addAttachment, fileToUpload2); + + el.$$('.attachment-delete').get(0).click(); + + let newCountAttachments = await el.$$('.single-attachment').count(); + + expect(countAttachments + 1).to.be.equal(newCountAttachments); +}; diff --git a/e2e/helpers/custom-fields-helper.js b/e2e/helpers/custom-fields-helper.js index c5607f10..5420a98e 100644 --- a/e2e/helpers/custom-fields-helper.js +++ b/e2e/helpers/custom-fields-helper.js @@ -55,11 +55,12 @@ helper.getCustomFiledsByType = function(indexType) { helper.delete = async function(indexType, indexCustomField) { let customField = helper.getCustomFiledsByType(indexType).get(indexCustomField); - let count = await helper.getCustomFiledsByType(indexType).count(); + browser.actions() + .mouseMove(customField.$('.js-delete-custom-field-button')) + .click() + .perform(); - customField.$('.js-delete-custom-field-button').click(); - - utils.lightbox.confirm.ok(); + return utils.lightbox.confirm.ok(); }; helper.getName = function(indexType, indexCustomField) { diff --git a/e2e/helpers/detail-helper.js b/e2e/helpers/detail-helper.js index 370d6804..81c16bf1 100644 --- a/e2e/helpers/detail-helper.js +++ b/e2e/helpers/detail-helper.js @@ -8,12 +8,16 @@ helper.title = function() { el: el, getTitle: function() { - return el.$('.view-subject').getText(); + return el.$('.view-subject').getText(); }, setTitle: function(title) { - el.$('.view-subject').click(); - el.$('.edit-subject input').clear().sendKeys(title); + el.$('.view-subject').click(); + el.$('.edit-subject input').clear().sendKeys(title); + }, + + save: function() { + el.$('.save').click(); } }; @@ -94,16 +98,15 @@ helper.statusSelector = function() { el: el, setStatus: async function(value) { - let status = el.$('.status-data'); + let status = el.$('.detail-status-inner'); + await utils.popover.open(status, value); - return this.getSelectedStatus() + + return this.getSelectedStatus(); }, getSelectedStatus: async function(){ - return el.$('.status-status').getInnerHtml(); - }, - getGeneralStatus: async function(){ - return el.$('.detail-status').getInnerHtml(); - }, + return el.$$('.detail-status-inner span').first().getInnerHtml(); + } }; return obj; @@ -115,10 +118,12 @@ helper.assignedTo = function() { let obj = { el: el, clear: async function() { - await browser.actions().mouseMove(el).perform(); - if (await el.$('.icon-delete').isPresent()) { - el.$('.icon-delete').click(); + await browser.actions() + .mouseMove(el.$('.icon-delete')) + .click() + .perform(); + await utils.lightbox.confirm.ok(); await browser.waitForAngular(); } @@ -128,6 +133,9 @@ helper.assignedTo = function() { }, getUserName: function() { return el.$('.user-assigned').getText(); + }, + isUnassigned: function() { + return el.$('.assign-to-me').isPresent(); } }; @@ -256,14 +264,21 @@ helper.delete = function() { }; helper.attachment = function() { - let el = $('tg-attachments'); + let el = $('tg-attachments-full'); let obj = { el:el, + waitEditableClose: function() { + return browser.wait(async () => { + let editableAttachmentsCount = await $$('tg-attachment .editable-attachment-comment').count(); + + return !editableAttachmentsCount; + }, 5000); + }, upload: async function(filePath, name) { let addAttach = el.$('#add-attach'); - let countAttachments = await $$('div[tg-attachment]').count(); + let countAttachments = await $$('tg-attachment').count(); let toggleInput = function() { $('#add-attach').toggle(); @@ -274,37 +289,39 @@ helper.attachment = function() { await browser.waitForAngular(); await browser.wait(async () => { - let newCountAttachments = await $$('div[tg-attachment]').count(); + let count = await $$('tg-attachment .editable-attachment-comment input').count(); - return newCountAttachments == countAttachments + 1; + return !!count; }, 5000); - await el.$$('div[tg-attachment] .editable-attachment-comment input').last().sendKeys(name); + await el.$$('tg-attachment .editable-attachment-comment input').last().sendKeys(name); await browser.actions().sendKeys(protractor.Key.ENTER).perform(); await browser.executeScript(toggleInput); await browser.waitForAngular(); + await obj.waitEditableClose(); }, renameLastAttchment: async function (name) { - await browser.actions().mouseMove(el.$$('div[tg-attachment]').last()).perform(); - await el.$$('div[tg-attachment] .attachment-settings .icon-edit').last().click(); - await el.$$('div[tg-attachment] .editable-attachment-comment input').last().sendKeys(name); + await browser.actions().mouseMove(el.$$('tg-attachment').last()).perform(); + await el.$$('tg-attachment .attachment-settings .icon-edit').last().click(); + await el.$$('tg-attachment .editable-attachment-comment input').last().sendKeys(name); await browser.actions().sendKeys(protractor.Key.ENTER).perform(); - return browser.waitForAngular(); + await browser.waitForAngular(); + await obj.waitEditableClose(); }, getFirstAttachmentName: async function () { - let name = await el.$$('div[tg-attachment] .attachment-comments').first().getText(); + let name = await el.$$('tg-attachment .attachment-comments').first().getText(); return name; }, getLastAttachmentName: async function () { - let name = await el.$$('div[tg-attachment] .attachment-comments').last().getText(); + let name = await el.$$('tg-attachment .attachment-comments').last().getText(); return name; }, countAttachments: async function(){ - return await el.$$('div[tg-attachment]').count(); + return await el.$$('tg-attachment').count(); }, countDeprecatedAttachments: async function(){ @@ -321,10 +338,10 @@ helper.attachment = function() { }, deprecateLastAttachment: async function() { - await browser.actions().mouseMove(el.$$('div[tg-attachment]').last()).perform(); - await el.$$('div[tg-attachment] .attachment-settings .icon-edit').last().click(); - await el.$$('div[tg-attachment] .editable-attachment-deprecated input').last().click(); - await el.$$('div[tg-attachment] .attachment-settings .editable-settings.icon-floppy').last().click(); + await browser.actions().mouseMove(el.$$('tg-attachment').last()).perform(); + await el.$$('tg-attachment .attachment-settings .icon-edit').last().click(); + await el.$$('tg-attachment .editable-attachment-deprecated input').last().click(); + await el.$$('tg-attachment .attachment-settings .editable-settings.icon-floppy').last().click(); await browser.waitForAngular(); }, @@ -333,7 +350,7 @@ helper.attachment = function() { }, deleteLastAttachment: async function() { - let attachment = await $$('div[tg-attachment]').last(); + let attachment = await $$('tg-attachment').last(); await browser.actions().mouseMove(attachment).perform(); @@ -359,11 +376,23 @@ helper.attachment = function() { }, dragLastAttchmentToFirstPosition: async function() { - await browser.actions().mouseMove(el.$$('div[tg-attachment]').last()).perform(); - let lastDraggableAttachment = el.$$('div[tg-attachment] .attachment-settings .icon-drag-v').last(); - let destination = el.$$('div[tg-attachment] .attachment-settings .icon-drag-v').first(); + await browser.actions().mouseMove(el.$$('tg-attachment').last()).perform(); + let lastDraggableAttachment = el.$$('tg-attachment .attachment-settings .icon-drag-v').last(); + let destination = el.$$('tg-attachment .attachment-settings .icon-drag-v').first(); await utils.common.drag(lastDraggableAttachment, destination); - } + }, + + galleryImages: function() { + return $$('tg-attachment-gallery'); + }, + + gallery: function() { + $('.view-gallery').click(); + }, + + list: function() { + $('.view-list').click(); + }, }; return obj; @@ -372,7 +401,7 @@ helper.attachment = function() { helper.watchers = function() { - let el = $('.ticket-track-buttons .ticket-watchers'); + let el = $('.ticket-watch-buttons'); let obj = { el: el, diff --git a/e2e/helpers/discover-helper.js b/e2e/helpers/discover-helper.js new file mode 100644 index 00000000..a6034289 --- /dev/null +++ b/e2e/helpers/discover-helper.js @@ -0,0 +1,87 @@ +var utils = require('../utils'); + +var helper = module.exports; + +helper.liked = function() { + return $('tg-most-liked'); +}; + +helper.active = function() { + return $('tg-most-active'); +}; + +helper.featured = function() { + return $('tg-featured-projects'); +}; + +helper.likedProjects = function() { + return helper.liked().$$('.highlighted-project'); +}; + +helper.activeProjects = function() { + return helper.active().$$('.highlighted-project'); +}; + +helper.featuredProjects = function() { + return helper.featured().$$('.featured-project'); +}; + +helper.rearrangeLike = function(index) { + helper.liked().$('.current-filter').click(); + + helper.liked().$$('.filter-list li').get(index).click(); +}; + +helper.getLikeFilterText = function(index) { + return helper.liked().$('.current-filter').getText(); +}; + +helper.rearrangeActive = function(index) { + helper.active().$('.current-filter').click(); + + helper.active().$$('.filter-list li').get(index).click(); +}; + +helper.getActiveFilterText = function(index) { + return helper.active().$('.current-filter').getText(); +}; + +helper.searchFilter = function(index) { + return $$('.searchbox-filters label').get(index).click(); +}; + +helper.searchProjectsList = function() { + return $('.project-list'); +}; + +helper.searchProjects = function() { + return helper.searchProjectsList().$$('li'); +}; + +helper.searchInput = function() { + return $('.searchbox input'); +}; + +helper.sendSearch = function() { + return $('.search-button').click(); +}; + +helper.mostLiked = function() { + $$('.discover-search-filter').get(0).click(); +}; + +helper.mostActived = function() { + $$('.discover-search-filter').get(1).click(); +}; + +helper.searchOrder = function(index) { + $$('.filter-list a').get(index).click(); +}; + +helper.orderSelectorWrapper = function() { + return $('.discover-search-subfilter'); +}; + +helper.clearOrder = function() { + helper.orderSelectorWrapper().$('.results').click(); +}; diff --git a/e2e/helpers/project-detail-helper.js b/e2e/helpers/project-detail-helper.js new file mode 100644 index 00000000..096dab63 --- /dev/null +++ b/e2e/helpers/project-detail-helper.js @@ -0,0 +1,27 @@ +var utils = require('../utils'); + +var helper = module.exports; + +helper.lookingForPeople = function() { + return $$('.looking-for-people input').get(0); +}; + +helper.lookingForPeopleReason = function() { + return $$('.looking-for-people-reason input').get(0); +}; + +helper.toggleIsLookingForPeople = function() { + helper.lookingForPeople().click(); +}; + +helper.editLogo = function() { + let inputFile = $('#logo-field'); + + var fileToUpload = utils.common.uploadImagePath(); + + return utils.common.uploadFile(inputFile, fileToUpload); +}; + +helper.getLogoSrc = function() { + return $('.image-container .image'); +}; diff --git a/e2e/helpers/taskboard-helper.js b/e2e/helpers/taskboard-helper.js index 88f598c8..6ca5c2b8 100644 --- a/e2e/helpers/taskboard-helper.js +++ b/e2e/helpers/taskboard-helper.js @@ -84,7 +84,7 @@ helper.getCreateTask = function() { return el.$('.blocked'); }, blockedNote: function() { - return el.$('textarea[name="blocked_note"]'); + return el.$('input[name="blocked_note"]'); }, submit: function() { el.$('button[type="submit"]').click(); diff --git a/e2e/helpers/us-detail-helper.js b/e2e/helpers/us-detail-helper.js index 946bb6ef..ebc1ff80 100644 --- a/e2e/helpers/us-detail-helper.js +++ b/e2e/helpers/us-detail-helper.js @@ -43,32 +43,30 @@ helper.clientRequirement = function() { return obj; }; -helper.relatedTaskForm = function(form, name, status, assigned_to) { +helper.relatedTaskForm = async function(form, name, status, assigned_to) { form.$('input').sendKeys(name); let taskStatus = form.$('.task-status'); - utils.popover.open(taskStatus, status); + await utils.popover.open(taskStatus, status); form.$('.task-assignedto').click(); let assignToLightbox = commonHelper.assignToLightbox(); - assignToLightbox.waitOpen(); - - assignToLightbox.selectFirst(); - - assignToLightbox.waitClose(); + await assignToLightbox.waitOpen(); + await assignToLightbox.selectFirst(); + await assignToLightbox.waitClose(); form.$('.icon-floppy').click(); }; helper.createRelatedTasks = function(name, status, assigned_to) { - $$('.related-tasks-buttons').get(0).click(); + $('section[tg-related-tasks] .add-button').click(); let form = $('.related-task-create-form'); - helper.relatedTaskForm(form, status, assigned_to); + return helper.relatedTaskForm(form, status, assigned_to); }; helper.editRelatedTasks = function(taskIndex, name, status, assigned_to) { @@ -76,7 +74,11 @@ helper.editRelatedTasks = function(taskIndex, name, status, assigned_to) { task.$('.icon-edit').click(); - helper.relatedTaskForm(task, status, assigned_to); + return helper.relatedTaskForm(task, status, assigned_to); +}; + +helper.editRelatedTasksEnabled = function() { + return $$('related-task-create-form.active'); }; helper.deleteRelatedTask = function(taskIndex, name, status, assigned_to) { @@ -88,7 +90,7 @@ helper.deleteRelatedTask = function(taskIndex, name, status, assigned_to) { .click() .perform(); - utils.lightbox.confirm.ok(); + return utils.lightbox.confirm.ok(); }; helper.relatedTasks = function() { diff --git a/e2e/screenshots_old/auth/delete-account.png b/e2e/screenshots_old/auth/delete-account.png deleted file mode 100644 index 9049e866..00000000 Binary files a/e2e/screenshots_old/auth/delete-account.png and /dev/null differ diff --git a/e2e/screenshots_old/auth/login.png b/e2e/screenshots_old/auth/login.png deleted file mode 100644 index d4b12126..00000000 Binary files a/e2e/screenshots_old/auth/login.png and /dev/null differ diff --git a/e2e/screenshots_old/auth/register-validation.png b/e2e/screenshots_old/auth/register-validation.png deleted file mode 100644 index 42a65746..00000000 Binary files a/e2e/screenshots_old/auth/register-validation.png and /dev/null differ diff --git a/e2e/screenshots_old/auth/register.png b/e2e/screenshots_old/auth/register.png deleted file mode 100644 index c5079a35..00000000 Binary files a/e2e/screenshots_old/auth/register.png and /dev/null differ diff --git a/e2e/screenshots_old/auth/remember-password-success.png b/e2e/screenshots_old/auth/remember-password-success.png deleted file mode 100644 index 603195a1..00000000 Binary files a/e2e/screenshots_old/auth/remember-password-success.png and /dev/null differ diff --git a/e2e/screenshots_old/auth/remember-password.png b/e2e/screenshots_old/auth/remember-password.png deleted file mode 100644 index 4e21f916..00000000 Binary files a/e2e/screenshots_old/auth/remember-password.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/backlog-filters.png b/e2e/screenshots_old/backlog/backlog-filters.png deleted file mode 100644 index 8cdf08f0..00000000 Binary files a/e2e/screenshots_old/backlog/backlog-filters.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/backlog-tags.png b/e2e/screenshots_old/backlog/backlog-tags.png deleted file mode 100644 index 5e56c5bb..00000000 Binary files a/e2e/screenshots_old/backlog/backlog-tags.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/backlog.png b/e2e/screenshots_old/backlog/backlog.png deleted file mode 100644 index 4525b9c4..00000000 Binary files a/e2e/screenshots_old/backlog/backlog.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/create-milestone.png b/e2e/screenshots_old/backlog/create-milestone.png deleted file mode 100644 index 5917c29d..00000000 Binary files a/e2e/screenshots_old/backlog/create-milestone.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/create-us-filled.png b/e2e/screenshots_old/backlog/create-us-filled.png deleted file mode 100644 index ac9181d1..00000000 Binary files a/e2e/screenshots_old/backlog/create-us-filled.png and /dev/null differ diff --git a/e2e/screenshots_old/backlog/create-us.png b/e2e/screenshots_old/backlog/create-us.png deleted file mode 100644 index c71cb967..00000000 Binary files a/e2e/screenshots_old/backlog/create-us.png and /dev/null differ diff --git a/e2e/screenshots_old/chrome/.gitignore b/e2e/screenshots_old/chrome/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/e2e/screenshots_old/chrome/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/e2e/screenshots_old/firefox/.gitignore b/e2e/screenshots_old/firefox/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/e2e/screenshots_old/firefox/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/e2e/screenshots_old/home/dashboard.png b/e2e/screenshots_old/home/dashboard.png deleted file mode 100644 index 6e97fe91..00000000 Binary files a/e2e/screenshots_old/home/dashboard.png and /dev/null differ diff --git a/e2e/screenshots_old/home/projects.png b/e2e/screenshots_old/home/projects.png deleted file mode 100644 index d29794c1..00000000 Binary files a/e2e/screenshots_old/home/projects.png and /dev/null differ diff --git a/e2e/screenshots_old/ie/.gitignore b/e2e/screenshots_old/ie/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/e2e/screenshots_old/ie/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/e2e/screenshots_old/issues/detail updated.png b/e2e/screenshots_old/issues/detail updated.png deleted file mode 100644 index 017dffa1..00000000 Binary files a/e2e/screenshots_old/issues/detail updated.png and /dev/null differ diff --git a/e2e/screenshots_old/issues/detail.png b/e2e/screenshots_old/issues/detail.png deleted file mode 100644 index f1a09176..00000000 Binary files a/e2e/screenshots_old/issues/detail.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/archive-open.png b/e2e/screenshots_old/kanban/archive-open.png deleted file mode 100644 index bbd8a950..00000000 Binary files a/e2e/screenshots_old/kanban/archive-open.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/archive.png b/e2e/screenshots_old/kanban/archive.png deleted file mode 100644 index d2a54cf7..00000000 Binary files a/e2e/screenshots_old/kanban/archive.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/create-us.png b/e2e/screenshots_old/kanban/create-us.png deleted file mode 100644 index e2c67f1b..00000000 Binary files a/e2e/screenshots_old/kanban/create-us.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/edit-us.png b/e2e/screenshots_old/kanban/edit-us.png deleted file mode 100644 index f5ff0a73..00000000 Binary files a/e2e/screenshots_old/kanban/edit-us.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/fold-cards.png b/e2e/screenshots_old/kanban/fold-cards.png deleted file mode 100644 index def85c5f..00000000 Binary files a/e2e/screenshots_old/kanban/fold-cards.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/fold-column.png b/e2e/screenshots_old/kanban/fold-column.png deleted file mode 100644 index ebace3d0..00000000 Binary files a/e2e/screenshots_old/kanban/fold-column.png and /dev/null differ diff --git a/e2e/screenshots_old/kanban/kanban.png b/e2e/screenshots_old/kanban/kanban.png deleted file mode 100644 index 15e538bf..00000000 Binary files a/e2e/screenshots_old/kanban/kanban.png and /dev/null differ diff --git a/e2e/screenshots_old/project/home.png b/e2e/screenshots_old/project/home.png deleted file mode 100644 index 030acf2b..00000000 Binary files a/e2e/screenshots_old/project/home.png and /dev/null differ diff --git a/e2e/screenshots_old/safari/.gitignore b/e2e/screenshots_old/safari/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/e2e/screenshots_old/safari/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/e2e/screenshots_old/taskboard/create-task-filled.png b/e2e/screenshots_old/taskboard/create-task-filled.png deleted file mode 100644 index 55983e47..00000000 Binary files a/e2e/screenshots_old/taskboard/create-task-filled.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/create-task.png b/e2e/screenshots_old/taskboard/create-task.png deleted file mode 100644 index 950c5670..00000000 Binary files a/e2e/screenshots_old/taskboard/create-task.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/edit-task-filled.png b/e2e/screenshots_old/taskboard/edit-task-filled.png deleted file mode 100644 index f8102976..00000000 Binary files a/e2e/screenshots_old/taskboard/edit-task-filled.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/edit-task.png b/e2e/screenshots_old/taskboard/edit-task.png deleted file mode 100644 index 375e0742..00000000 Binary files a/e2e/screenshots_old/taskboard/edit-task.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/fold-column-row.png b/e2e/screenshots_old/taskboard/fold-column-row.png deleted file mode 100644 index e76b077c..00000000 Binary files a/e2e/screenshots_old/taskboard/fold-column-row.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/fold-column.png b/e2e/screenshots_old/taskboard/fold-column.png deleted file mode 100644 index 039c5d07..00000000 Binary files a/e2e/screenshots_old/taskboard/fold-column.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/fold-row.png b/e2e/screenshots_old/taskboard/fold-row.png deleted file mode 100644 index a457c271..00000000 Binary files a/e2e/screenshots_old/taskboard/fold-row.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/grap-open.png b/e2e/screenshots_old/taskboard/grap-open.png deleted file mode 100644 index 139f239d..00000000 Binary files a/e2e/screenshots_old/taskboard/grap-open.png and /dev/null differ diff --git a/e2e/screenshots_old/taskboard/taskboard.png b/e2e/screenshots_old/taskboard/taskboard.png deleted file mode 100644 index 974fdd08..00000000 Binary files a/e2e/screenshots_old/taskboard/taskboard.png and /dev/null differ diff --git a/e2e/screenshots_old/tasks/detail updated.png b/e2e/screenshots_old/tasks/detail updated.png deleted file mode 100644 index 8cc838cd..00000000 Binary files a/e2e/screenshots_old/tasks/detail updated.png and /dev/null differ diff --git a/e2e/screenshots_old/tasks/detail.png b/e2e/screenshots_old/tasks/detail.png deleted file mode 100644 index c42b287c..00000000 Binary files a/e2e/screenshots_old/tasks/detail.png and /dev/null differ diff --git a/e2e/screenshots_old/team/after-leaving.png b/e2e/screenshots_old/team/after-leaving.png deleted file mode 100644 index a41259b4..00000000 Binary files a/e2e/screenshots_old/team/after-leaving.png and /dev/null differ diff --git a/e2e/screenshots_old/team/filtering-by-role.png b/e2e/screenshots_old/team/filtering-by-role.png deleted file mode 100644 index 160a6af4..00000000 Binary files a/e2e/screenshots_old/team/filtering-by-role.png and /dev/null differ diff --git a/e2e/screenshots_old/team/searching-by-name.png b/e2e/screenshots_old/team/searching-by-name.png deleted file mode 100644 index e6007b10..00000000 Binary files a/e2e/screenshots_old/team/searching-by-name.png and /dev/null differ diff --git a/e2e/screenshots_old/team/team.png b/e2e/screenshots_old/team/team.png deleted file mode 100644 index 7ec1101e..00000000 Binary files a/e2e/screenshots_old/team/team.png and /dev/null differ diff --git a/e2e/screenshots_old/user-stories/detail updated.png b/e2e/screenshots_old/user-stories/detail updated.png deleted file mode 100644 index 52554197..00000000 Binary files a/e2e/screenshots_old/user-stories/detail updated.png and /dev/null differ diff --git a/e2e/screenshots_old/user-stories/detail.png b/e2e/screenshots_old/user-stories/detail.png deleted file mode 100644 index fb59d862..00000000 Binary files a/e2e/screenshots_old/user-stories/detail.png and /dev/null differ diff --git a/e2e/screenshots_old/wiki/deleting-the-created-link.png b/e2e/screenshots_old/wiki/deleting-the-created-link.png deleted file mode 100644 index 991c80da..00000000 Binary files a/e2e/screenshots_old/wiki/deleting-the-created-link.png and /dev/null differ diff --git a/e2e/screenshots_old/wiki/empty.png b/e2e/screenshots_old/wiki/empty.png deleted file mode 100644 index 02d9c249..00000000 Binary files a/e2e/screenshots_old/wiki/empty.png and /dev/null differ diff --git a/e2e/screenshots_old/wiki/home-edition-preview.png b/e2e/screenshots_old/wiki/home-edition-preview.png deleted file mode 100644 index 34ef06d2..00000000 Binary files a/e2e/screenshots_old/wiki/home-edition-preview.png and /dev/null differ diff --git a/e2e/screenshots_old/wiki/home-edition.png b/e2e/screenshots_old/wiki/home-edition.png deleted file mode 100644 index 10b94df5..00000000 Binary files a/e2e/screenshots_old/wiki/home-edition.png and /dev/null differ diff --git a/e2e/screenshots_old/wiki/new-link-created-with-empty-wiki-page.png b/e2e/screenshots_old/wiki/new-link-created-with-empty-wiki-page.png deleted file mode 100644 index 3c8af836..00000000 Binary files a/e2e/screenshots_old/wiki/new-link-created-with-empty-wiki-page.png and /dev/null differ diff --git a/e2e/shared/detail.js b/e2e/shared/detail.js index 1bfeece9..5f6dc991 100644 --- a/e2e/shared/detail.js +++ b/e2e/shared/detail.js @@ -17,9 +17,15 @@ shared.titleTesting = async function() { let titleHelper = detailHelper.title(); let title = await titleHelper.getTitle(); let date = Date.now(); + titleHelper.setTitle("New title " + date); + titleHelper.save(); + let newTitle = await titleHelper.getTitle(); + expect(notifications.success.open()).to.be.eventually.true; expect(newTitle).to.be.not.equal(title); + + await notifications.success.close(); } shared.tagsTesting = async function() { @@ -28,7 +34,8 @@ shared.tagsTesting = async function() { await tagsHelper.clearTags(); let date = Date.now(); let tags = [1, 2, 3].map((i) => date + "-" + i); - tagsHelper.addTags(tags); + + await tagsHelper.addTags(tags); let newtagsText = await tagsHelper.getTagsText(); @@ -42,38 +49,35 @@ shared.descriptionTesting = async function() { descriptionHelper.enabledEditionMode(); descriptionHelper.setText("New description " + date); descriptionHelper.save(); + let newDescription = await descriptionHelper.getInnerHtml(); + + expect(notifications.success.open()).to.be.eventually.true; expect(newDescription).to.be.not.equal(description); + + await notifications.success.close(); } -shared.statusTesting = async function() { +shared.statusTesting = async function(status1 , status2) { let statusHelper = detailHelper.statusSelector(); - // Current status - let selectedStatus = await statusHelper.getSelectedStatus(); - let genericStatus = await statusHelper.getGeneralStatus(); - expect(selectedStatus).to.be.equal(genericStatus); - // Status 1 await statusHelper.setStatus(1); - selectedStatus = await statusHelper.getSelectedStatus(); - genericStatus = await statusHelper.getGeneralStatus(); - expect(selectedStatus).to.be.equal(genericStatus); + let selectedStatus = await statusHelper.getSelectedStatus(); + expect(selectedStatus).to.be.equal(status1); // Status 2 await statusHelper.setStatus(2); let newSelectedStatus = await statusHelper.getSelectedStatus(); - let newGenericStatus = await statusHelper.getGeneralStatus(); - expect(newSelectedStatus).to.be.equal(newGenericStatus); - expect(newSelectedStatus).to.be.not.equal(selectedStatus); - expect(newGenericStatus).to.be.not.equal(genericStatus); + expect(newSelectedStatus).to.be.equal(status2); } shared.assignedToTesting = function() { before(function () { let assignedTo = detailHelper.assignedTo(); + return assignedTo.clear(); }); @@ -97,13 +101,10 @@ shared.assignedToTesting = function() { it('unassign', async function() { let assignedTo = detailHelper.assignedTo(); - let assignToLightbox = commonHelper.assignToLightbox(); await assignedTo.clear(); - let newUserName = await assignedTo.getUserName(); - - expect(newUserName).to.be.equal('Not assigned'); + expect(assignedTo.isUnassigned()).to.be.eventually.true; }); it('filter', async function () { @@ -206,6 +207,8 @@ shared.blockTesting = async function() { blockHelper.unblock(); expect($('.block-description').isDisplayed()).to.be.eventually.false; + + await notifications.success.close(); } shared.attachmentTesting = async function() { @@ -217,19 +220,22 @@ shared.attachmentTesting = async function() { var fileToUpload = commonUtil.uploadFilePath(); await attachmentHelper.upload(fileToUpload, 'This is the testing name ' + date); + await browser.sleep(5000); // Check set name let name = await attachmentHelper.getLastAttachmentName(); expect(name).to.be.equal('This is the testing name ' + date); + // Check new length let newAttachmentsLength = await attachmentHelper.countAttachments(); expect(newAttachmentsLength).to.be.equal(attachmentsLength + 1); //Drag'n drop - await attachmentHelper.dragLastAttchmentToFirstPosition(); - name = await attachmentHelper.getFirstAttachmentName(); - expect(name).to.be.equal('This is the testing name ' + date); - + if (['firefox', 'internet explorer'].indexOf(browser.browserName) === -1) { + await attachmentHelper.dragLastAttchmentToFirstPosition(); + name = await attachmentHelper.getFirstAttachmentName(); + expect(name).to.be.equal('This is the testing name ' + date); + } // Renaming await attachmentHelper.renameLastAttchment('This is the new testing name ' + date); name = await attachmentHelper.getLastAttachmentName(); @@ -249,6 +255,17 @@ shared.attachmentTesting = async function() { newAttachmentsLength = await attachmentHelper.countAttachments(); expect(newAttachmentsLength).to.be.equal(attachmentsLength + deprecatedAttachmentsLength); + // Gallery + attachmentHelper.gallery(); + + let countImages = await attachmentHelper.galleryImages().count(); + + commonUtil.takeScreenshot('attachments', 'gallery'); + + expect(countImages).to.be.above(0); + + attachmentHelper.list(); + // Deleting attachmentsLength = await attachmentHelper.countAttachments(); await attachmentHelper.deleteLastAttachment(); @@ -348,29 +365,28 @@ shared.watchersTesting = function() { shared.customFields = function(typeIndex) { before(async function() { let url = await browser.getCurrentUrl(); - let rootUrl = await commonUtil.getProjectUrlRoot(); - browser.get(rootUrl + '/admin/project-values/custom-fields'); - commonUtil.waitLoader(); - - customFieldsHelper.create(typeIndex, 'detail-test-custom-fields-text', 'desc1', 1); + await customFieldsHelper.create(typeIndex, 'detail-test-custom-fields-text', 'desc1', 1); // debounce :( - browser.sleep(2000); + await browser.sleep(2000); - customFieldsHelper.create(typeIndex, 'detail-test-custom-fields-multi', 'desc1', 3); + await customFieldsHelper.create(typeIndex, 'detail-test-custom-fields-multi', 'desc1', 3); // debounce :( - browser.sleep(2000); + await browser.sleep(2000); browser.get(url); - commonUtil.waitLoader(); + + await commonUtil.waitLoader(); }); it('text create', async function() { let customFields = customFieldsHelper.getDetailFields(); + + // await browser.sleep(4000); let count = await customFields.count(); let textField = customFields.get(count - 2); diff --git a/e2e/suites/admin/attributes/custom-fields.e2e.js b/e2e/suites/admin/attributes/custom-fields.e2e.js index 45d403df..9a6e008b 100644 --- a/e2e/suites/admin/attributes/custom-fields.e2e.js +++ b/e2e/suites/admin/attributes/custom-fields.e2e.js @@ -64,9 +64,9 @@ describe('custom-fields', function() { it('delete', async function() { let oldCountCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); - customFieldsHelper.delete(typeIndex, 0); + await customFieldsHelper.delete(typeIndex, 0); - browser.wait(async function() { + await browser.wait(async function() { let countCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); return countCustomFields === oldCountCustomFields - 1; @@ -84,13 +84,13 @@ describe('custom-fields', function() { // debounce :( await utils.notifications.success.open(); - await browser.sleep(2000); + await browser.sleep(2500); customFieldsHelper.create(typeIndex, 'test1-multi', 'desc1', 3); // debounce :( await utils.notifications.success.open(); - await browser.sleep(2000); + await browser.sleep(2500); // customFieldsHelper.create(typeIndex, 'test1-date', 'desc1', 4); @@ -122,9 +122,9 @@ describe('custom-fields', function() { it('delete', async function() { let oldCountCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); - customFieldsHelper.delete(typeIndex, 0); + await customFieldsHelper.delete(typeIndex, 0); - browser.wait(async function() { + await browser.wait(async function() { let countCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); return countCustomFields === oldCountCustomFields - 1; @@ -180,9 +180,9 @@ describe('custom-fields', function() { it('delete', async function() { let oldCountCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); - customFieldsHelper.delete(typeIndex, 0); + await customFieldsHelper.delete(typeIndex, 0); - browser.wait(async function() { + await browser.wait(async function() { let countCustomFields = await customFieldsHelper.getCustomFiledsByType(typeIndex).count(); return countCustomFields === oldCountCustomFields - 1; diff --git a/e2e/suites/admin/attributes/priorities.e2e.js b/e2e/suites/admin/attributes/priorities.e2e.js index 65a4f49c..78375b28 100644 --- a/e2e/suites/admin/attributes/priorities.e2e.js +++ b/e2e/suites/admin/attributes/priorities.e2e.js @@ -12,7 +12,7 @@ describe('attributes - priorities', function() { before(async function(){ browser.get(browser.params.glob.host + 'project/project-0/admin/project-values/priorities'); - await utils.common.waitLoader(); + await adminAttributesHelper.waitLoad(); utils.common.takeScreenshot('attributes', 'priorities'); }); diff --git a/e2e/suites/admin/attributes/severities.e2e.js b/e2e/suites/admin/attributes/severities.e2e.js index cff219d0..49d356cf 100644 --- a/e2e/suites/admin/attributes/severities.e2e.js +++ b/e2e/suites/admin/attributes/severities.e2e.js @@ -12,7 +12,7 @@ describe('attributes - severities', function() { before(async function(){ browser.get(browser.params.glob.host + 'project/project-0/admin/project-values/severities'); - await utils.common.waitLoader(); + await adminAttributesHelper.waitLoad(); utils.common.takeScreenshot('attributes', 'severities'); }); diff --git a/e2e/suites/admin/attributes/status.e2e.js b/e2e/suites/admin/attributes/status.e2e.js index 89f5378c..33d1b2c9 100644 --- a/e2e/suites/admin/attributes/status.e2e.js +++ b/e2e/suites/admin/attributes/status.e2e.js @@ -12,7 +12,7 @@ describe('attributes - status', function() { before(async function(){ browser.get(browser.params.glob.host + 'project/project-0/admin/project-values/status'); - await utils.common.waitLoader(); + await adminAttributesHelper.waitLoad(); utils.common.takeScreenshot('attributes', 'status'); }); diff --git a/e2e/suites/admin/attributes/types.e2e.js b/e2e/suites/admin/attributes/types.e2e.js index c6d3b309..bafa68df 100644 --- a/e2e/suites/admin/attributes/types.e2e.js +++ b/e2e/suites/admin/attributes/types.e2e.js @@ -12,7 +12,7 @@ describe('attributes - types', function() { before(async function(){ browser.get(browser.params.glob.host + 'project/project-0/admin/project-values/types'); - await utils.common.waitLoader(); + await adminAttributesHelper.waitLoad(); utils.common.takeScreenshot('attributes', 'types'); }); diff --git a/e2e/suites/admin/project/create-delete.e2e.js b/e2e/suites/admin/project/create-delete.e2e.js index 631704a5..06048212 100644 --- a/e2e/suites/admin/project/create-delete.e2e.js +++ b/e2e/suites/admin/project/create-delete.e2e.js @@ -55,7 +55,9 @@ describe('create-delete project', function() { let linkAdmin = $('#nav-admin a'); utils.common.link(linkAdmin); - await utils.common.waitLoader(); + browser.wait(function() { + return $('.project-details').isPresent(); + }); await createProject.delete(); await browser.waitForAngular(); diff --git a/e2e/suites/admin/project/project-detail.e2e.js b/e2e/suites/admin/project/project-detail.e2e.js index 827b562c..2ebb94a1 100644 --- a/e2e/suites/admin/project/project-detail.e2e.js +++ b/e2e/suites/admin/project/project-detail.e2e.js @@ -6,6 +6,8 @@ var chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); var expect = chai.expect; +var adminHelper = require('../../../helpers/project-detail-helper'); + describe('project detail', function() { before(async function(){ browser.get(browser.params.glob.host + 'project/project-0/admin/project-profile/details'); @@ -40,5 +42,40 @@ describe('project detail', function() { $('button[type="submit"]').click(); expect(utils.notifications.success.open()).to.be.eventually.equal(true); + + await utils.notifications.success.close(); + }); + + it('looking for people', async function() { + let checked = !! await adminHelper.lookingForPeople().getAttribute('checked'); + + if(checked) { + adminHelper.toggleIsLookingForPeople(); + } + + adminHelper.toggleIsLookingForPeople(); + + adminHelper.lookingForPeopleReason().sendKeys('looking for people reason'); + + $('button[type="submit"]').click(); + + checked = !! await adminHelper.lookingForPeople().getAttribute('checked'); + + expect(checked).to.be.true; + expect(utils.notifications.success.open()).to.be.eventually.equal(true); + }); + + it('edit logo', async function() { + let imageContainer = $('.image-container'); + + let htmlChanges = await utils.common.outerHtmlChanges(imageContainer); + + adminHelper.editLogo(); + + await htmlChanges(); + + let src = await adminHelper.getLogoSrc().getAttribute('src'); + + expect(src).to.contains('upload-image-test.png'); }); }); diff --git a/e2e/suites/auth/auth.e2e.js b/e2e/suites/auth/auth.e2e.js index f7b7b502..52f15d91 100644 --- a/e2e/suites/auth/auth.e2e.js +++ b/e2e/suites/auth/auth.e2e.js @@ -22,7 +22,11 @@ describe('auth', function() { $('.submit-button').click(); - expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host); + await utils.common.waitLoader(); + + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host); }); describe('page without perms', function() { @@ -35,7 +39,9 @@ describe('auth', function() { it("redirect to login", async function() { browser.get(browser.params.glob.host + path); - expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + 'login?next=' + encodeURIComponent('/' + path)); + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + 'login?next=' + encodeURIComponent('/' + path)); }); it("login redirect to the previous one", async function() { @@ -43,7 +49,9 @@ describe('auth', function() { $('input[name="password"]').sendKeys('123123'); $('.submit-button').click(); - expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + path); + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + path); }); }); @@ -60,7 +68,11 @@ describe('auth', function() { browser.actions().mouseMove($('div[tg-dropdown-user]')).perform(); $$('.dropdown-user li a').last().click(); - expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + 'login'); + await utils.common.waitLoader(); + + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + 'login'); }); describe("register", function() { @@ -105,7 +117,7 @@ describe('auth', function() { beforeEach(async function() { await utils.common.login(user.username, user.password); - browser.get(browser.params.glob.host + 'user-settings/user-change-password'); + return browser.get(browser.params.glob.host + 'user-settings/user-change-password'); }); it("error", function() { @@ -176,7 +188,9 @@ describe('auth', function() { $('.lightbox-delete-account .button-green').click(); - expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + 'login'); + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + 'login'); }); }); }); diff --git a/e2e/suites/backlog.e2e.js b/e2e/suites/backlog.e2e.js index 173d506d..1d7fd0d5 100644 --- a/e2e/suites/backlog.e2e.js +++ b/e2e/suites/backlog.e2e.js @@ -1,5 +1,6 @@ var utils = require('../utils'); var backlogHelper = require('../helpers').backlog; +var commonHelper = require('../helpers').common; var chai = require('chai'); var chaiAsPromised = require('chai-as-promised'); @@ -22,6 +23,7 @@ describe('backlog', function() { backlogHelper.openNewUs(); createUSLightbox = backlogHelper.getCreateEditUsLightbox(); + await createUSLightbox.waitOpen(); }); @@ -34,8 +36,8 @@ describe('backlog', function() { createUSLightbox.subject().sendKeys('subject'); // roles - createUSLightbox.setRole(1, 3); - createUSLightbox.setRole(3, 4); + await createUSLightbox.setRole(1, 3); + await createUSLightbox.setRole(3, 4); let totalPoints = await createUSLightbox.getRolePoints(); @@ -57,9 +59,12 @@ describe('backlog', function() { //settings createUSLightbox.settings(0).click(); - await utils.common.waitTransitionTime(createUSLightbox.settings(0)); + }); + it('upload attachments', commonHelper.lightboxAttachment); + + it('screenshots', function() { utils.common.takeScreenshot('backlog', 'create-us-filled'); }); @@ -70,6 +75,8 @@ describe('backlog', function() { await utils.lightbox.close(createUSLightbox.el); + await browser.waitForAngular(); + let newUsCount = await backlogHelper.userStories().count(); expect(newUsCount).to.be.equal(usCount + 1); @@ -124,10 +131,10 @@ describe('backlog', function() { editUSLightbox.subject().sendKeys('subjectedit'); // roles - editUSLightbox.setRole(1, 3); - editUSLightbox.setRole(2, 3); - editUSLightbox.setRole(3, 3); - editUSLightbox.setRole(4, 3); + await editUSLightbox.setRole(0, 3); + await editUSLightbox.setRole(1, 3); + await editUSLightbox.setRole(2, 3); + await editUSLightbox.setRole(3, 3); let totalPoints = await editUSLightbox.getRolePoints(); @@ -150,6 +157,8 @@ describe('backlog', function() { editUSLightbox.settings(1).click(); }); + it('upload attachments', commonHelper.lightboxAttachment); + it('send form', async function() { editUSLightbox.submit(); @@ -169,9 +178,13 @@ describe('backlog', function() { }); it('edit points inline', async function() { + let usPointsOriginal = await backlogHelper.getUsPoints(0, 1, 1); + await backlogHelper.setUsPoints(0, 1, 1); - expect(utils.notifications.success.open()).to.be.eventually.true; + let usPointsNew = await backlogHelper.getUsPoints(0); + + expect(usPointsOriginal).not.to.be.equal(usPointsNew); }); it('delete US', async function() { @@ -191,7 +204,6 @@ describe('backlog', function() { let dragElement = dragableElements.get(1); let dragElementHandler = dragElement.$('.icon-drag-v'); - let draggedElementRef = await backlogHelper.getUsRef(dragElement); await utils.common.drag(dragElementHandler, dragableElements.get(0)); @@ -202,7 +214,7 @@ describe('backlog', function() { expect(firstElementTextRef).to.be.equal(draggedElementRef); }); - it('reorder multiple us', async function() { + utils.common.browserSkip(['firefox', 'internet explorer'], 'reorder multiple us', async function() { let dragableElements = backlogHelper.userStories(); let count = await dragableElements.count(); @@ -231,7 +243,7 @@ describe('backlog', function() { expect(elementRef1).to.be.equal(draggedRefs[1]); }); - it('drag multiple us to milestone', async function() { + utils.common.browserSkip(['firefox', 'internet explorer'], 'drag multiple us to milestone', async function() { let sprint = backlogHelper.sprints().get(0); let initUssSprintCount = await backlogHelper.getSprintUsertories(sprint).count(); @@ -328,12 +340,13 @@ describe('backlog', function() { let firstInput = dragableElements.get(0).$('input[type="checkbox"]'); let lastInput = dragableElements.get(3).$('input[type="checkbox"]'); - browser.actions() + await browser.actions() .mouseMove(firstInput) .keyDown(protractor.Key.SHIFT) .click() .mouseMove(lastInput) .click() + .keyUp(protractor.Key.SHIFT) .perform(); let count = await backlogHelper.selectedUserStories().count(); @@ -543,7 +556,9 @@ describe('backlog', function() { await utils.common.drag(dragElement, dragableElements.get(0)); - expect(utils.notifications.error.open()).to.be.eventually.true; + let waitErrorOpen = await utils.notifications.error.open(); + + expect(waitErrorOpen).to.be.true; await utils.notifications.error.close(); }); @@ -556,7 +571,9 @@ describe('backlog', function() { await transition(); - expect(menu.getCssValue('width')).to.be.eventually.equal('0px'); + let waitWidth = await menu.getCssValue('width'); + + expect(waitWidth).to.be.equal('0px'); }); }); @@ -607,7 +624,7 @@ describe('backlog', function() { expect(closedSprints).to.be.equal(0); }); - it('open sprint by drag open US to closed sprint', async function() { + utils.common.browserSkip(['firefox', 'internet explorer'], 'open sprint by drag open US to closed sprint', async function() { backlogHelper.toggleClosedSprints(); await backlogHelper.setUsStatus(1, 0); diff --git a/e2e/suites/discover/discover-home.e2e.js b/e2e/suites/discover/discover-home.e2e.js new file mode 100644 index 00000000..60387e61 --- /dev/null +++ b/e2e/suites/discover/discover-home.e2e.js @@ -0,0 +1,65 @@ +var utils = require('../../utils'); +var discoverHelper = require('../../helpers/discover-helper'); + +var chai = require('chai'); +var chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +var expect = chai.expect; + + +describe('discover', () => { + before(async () => { + browser.get(browser.params.glob.host + 'discover'); + await utils.common.waitLoader(); + }); + + it('screenshot', async () => { + await utils.common.takeScreenshot("discover", "discover-home"); + }); + + describe('most liked', () => { + it('has projects', async () => { + let projects = discoverHelper.likedProjects(); + + let projectCount = await projects.count(); + + expect(projectCount).to.be.above(0); + }); + + it('rearrange', async () => { + discoverHelper.rearrangeLike(3); + + let filterText = await discoverHelper.getLikeFilterText(); + let projects = discoverHelper.likedProjects(); + + expect(filterText).to.be.equal('All time'); + expect(await projects.count()).to.be.equal(5); + + }); + }); + + describe('most active', () => { + it('has projects', async () => { + let projects = discoverHelper.activeProjects(); + + expect(await projects.count()).to.be.above(0); + }); + + it('rearrange', async () => { + discoverHelper.rearrangeActive(3); + + let filterText = await discoverHelper.getActiveFilterText(); + let projects = discoverHelper.activeProjects(); + + expect(filterText).to.be.equal('All time'); + expect(await projects.count()).to.be.equal(5); + }); + }); + + it('featured projects', async () => { + let projects = discoverHelper.featuredProjects(); + + expect(await projects.count()).to.be.above(0); + }); +}); diff --git a/e2e/suites/discover/discover-search.e2e.js b/e2e/suites/discover/discover-search.e2e.js new file mode 100644 index 00000000..9fa5bd02 --- /dev/null +++ b/e2e/suites/discover/discover-search.e2e.js @@ -0,0 +1,123 @@ +var utils = require('../../utils'); +var discoverHelper = require('../../helpers/discover-helper'); + +var chai = require('chai'); +var chaiAsPromised = require('chai-as-promised'); + +chai.use(chaiAsPromised); +var expect = chai.expect; + + +describe('discover search', () => { + before(async () => { + browser.get(browser.params.glob.host + 'discover/search'); + await utils.common.waitLoader(); + }); + + it('screenshot', async () => { + await utils.common.takeScreenshot("discover", "discover-search"); + }); + + describe('top bar', async () => { + after(async () => { + browser.get(browser.params.glob.host + 'discover/search'); + await utils.common.waitLoader(); + }); + + it('filters', async () => { + let htmlChanges = await utils.common.outerHtmlChanges(discoverHelper.searchProjectsList()); + + discoverHelper.searchFilter(3); + + await htmlChanges(); + + let url = await browser.getCurrentUrl(); + + let projects = discoverHelper.searchProjects(); + + expect(await projects.count()).to.be.above(0); + expect(url).to.be.equal(browser.params.glob.host + 'discover/search?filter=people'); + }); + + it('search by text', async () => { + discoverHelper.searchInput().sendKeys('Project Example 0'); + + discoverHelper.sendSearch(); + + let projects = discoverHelper.searchProjects(); + expect(await projects.count()).to.be.equal(1); + }); + }); + + describe('most liked', async () => { + after(async () => { + browser.get(browser.params.glob.host + 'discover/search'); + await utils.common.waitLoader(); + }); + + it('default', async () => { + discoverHelper.mostLiked(); + + utils.common.takeScreenshot("discover", "discover-search-filter"); + + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + 'discover/search?order_by=-total_fans_last_week'); + }); + + it('filter', async () => { + discoverHelper.searchOrder(3); + + let projects = discoverHelper.searchProjects(); + + let url = await browser.getCurrentUrl(); + + expect(await projects.count()).to.be.above(0); + expect(url).to.be.equal(browser.params.glob.host + 'discover/search?order_by=-total_fans'); + }); + + it('clear', async () => { + discoverHelper.clearOrder(); + + let orderSelector = discoverHelper.orderSelectorWrapper(); + + expect(await orderSelector.isPresent()).to.be.equal(false); + }); + }); + + describe('most active', async () => { + after(async () => { + browser.get(browser.params.glob.host + 'discover/search'); + await utils.common.waitLoader(); + }); + + it('default', async () => { + discoverHelper.mostActived(); + + utils.common.takeScreenshot("discover", "discover-search-filter"); + + let url = await browser.getCurrentUrl(); + + expect(url).to.be.equal(browser.params.glob.host + 'discover/search?order_by=-total_activity_last_week'); + }); + + it('filter', async () => { + discoverHelper.searchOrder(3); + + let projects = discoverHelper.searchProjects(); + + let url = await browser.getCurrentUrl(); + + expect(await projects.count()).to.be.above(0); + expect(url).to.be.equal(browser.params.glob.host + 'discover/search?order_by=-total_activity'); + }); + + it('clear', async () => { + discoverHelper.clearOrder(); + + let orderSelector = discoverHelper.orderSelectorWrapper(); + + expect(await orderSelector.isPresent()).to.be.equal(false); + }); + }); +}); diff --git a/e2e/suites/home.e2e.js b/e2e/suites/home.e2e.js index 1b659bfd..aecc8460 100644 --- a/e2e/suites/home.e2e.js +++ b/e2e/suites/home.e2e.js @@ -14,16 +14,16 @@ describe('home', function() { utils.common.takeScreenshot("home", "dashboard"); }); - it('working on filled', function() { - return expect($$('.working-on div[tg-duty]').count()).to.be.eventually.above(0); + it('working on filled', async function() { + return expect(await $$('.working-on div[tg-duty]').count()).to.be.above(0); }); - it('watching filled', function() { - return expect($$('.watching div[tg-duty]').count()).to.be.eventually.above(0); + it('watching filled', async function() { + return expect(await $$('.watching div[tg-duty]').count()).to.be.above(0); }); - it('project list filled', function() { - return expect($$('.home-project-list-single').count()).to.be.eventually.above(0); + it('project list filled', async function() { + return expect(await $$('.home-project').count()).to.be.above(0); }); describe('projects list', function() { @@ -34,16 +34,16 @@ describe('home', function() { utils.common.takeScreenshot("home", "projects"); }); - it('open create project lightbox', function() { + it('open create project lightbox', async function() { $('.master .create-project-btn').click(); - return expect(utils.lightbox.open('div[tg-lb-create-project]')).to.be.eventually.equal(true); + return expect(await utils.lightbox.open('div[tg-lb-create-project]')).to.be.equal(true); }); - it('close create project lightbox', function() { + it('close create project lightbox', async function() { $('div[tg-lb-create-project] .icon-delete').click(); - return expect(utils.lightbox.close('div[tg-lb-create-project]')).to.be.eventually.equal(true); + return expect(await utils.lightbox.close('div[tg-lb-create-project]')).to.be.equal(true); }); }); @@ -55,7 +55,7 @@ describe('home', function() { let dragableElements = element.all(by.css('.list-itemtype-project')); let dragElement = dragableElements.get(3); - let dragElementLink = dragElement.element(by.css('a')); + let dragElementLink = dragElement.element(by.css('h2 a')); await utils.common.waitLoader(); @@ -65,23 +65,23 @@ describe('home', function() { await browser.waitForAngular(); }); - utils.common.browserSkip('firefox', 'projects list has the new order', function() { - var firstElement = $$('.list-itemtype-project a').first().getText(); + utils.common.browserSkip('firefox', 'projects list has the new order', async function() { + var firstElement = await $$('.list-itemtype-project h2 a').first().getText(); - expect(firstElement).to.be.eventually.equal(draggedElementText); + expect(firstElement).to.be.equal(draggedElementText); }); - utils.common.browserSkip('firefox', 'projects menu has the new order', function() { - var firstElementText = $$('div[tg-dropdown-project-list] ul a').first().getInnerHtml(); + utils.common.browserSkip('firefox', 'projects menu has the new order', async function() { + var firstElementText = await $$('div[tg-dropdown-project-list] ul a').first().getInnerHtml(); - expect(firstElementText).to.be.eventually.equal(draggedElementText); + expect(firstElementText).to.be.equal(draggedElementText); }); after(async function() { //restore project position let dragableElements = element.all(by.css('.list-itemtype-project')); let dragElement = dragableElements.get(0); - let dragElementLink = dragElement.element(by.css('a')); + let dragElementLink = dragElement.element(by.css('h2 a')); await utils.common.waitLoader(); diff --git a/e2e/suites/issues/issue-detail.e2e.js b/e2e/suites/issues/issue-detail.e2e.js index 28467ab7..b713dbaa 100644 --- a/e2e/suites/issues/issue-detail.e2e.js +++ b/e2e/suites/issues/issue-detail.e2e.js @@ -7,7 +7,7 @@ var chaiAsPromised = require('chai-as-promised'); chai.use(chaiAsPromised); var expect = chai.expect; -describe.only('Issue detail', async function(){ +describe('Issue detail', async function(){ let issueUrl = ''; before(async function(){ @@ -31,7 +31,7 @@ describe.only('Issue detail', async function(){ it('description edition', sharedDetail.descriptionTesting); - it('status edition', sharedDetail.statusTesting); + it('status edition', sharedDetail.statusTesting.bind(this, 'In progress', 'Ready for test')); describe('assigned to edition', sharedDetail.assignedToTesting); diff --git a/e2e/suites/issues/issues.e2e.js b/e2e/suites/issues/issues.e2e.js index 199e1b1f..8eeb93f1 100644 --- a/e2e/suites/issues/issues.e2e.js +++ b/e2e/suites/issues/issues.e2e.js @@ -11,6 +11,7 @@ var expect = chai.expect; describe('issues list', function() { before(async function() { browser.get(browser.params.glob.host + 'project/project-3/issues'); + await utils.common.waitLoader(); utils.common.takeScreenshot('issues', 'issues'); @@ -41,7 +42,11 @@ describe('issues list', function() { await createIssueLightbox.tags().sendKeys('bbb'); browser.actions().sendKeys(protractor.Key.ENTER).perform(); + }); + it('upload attachments', commonHelper.lightboxAttachment); + + it('screenshots', function() { utils.common.takeScreenshot('issues', 'create-issue-filled'); }); @@ -51,6 +56,8 @@ describe('issues list', function() { await createIssueLightbox.waitClose(); expect(utils.notifications.success.open()).to.be.eventually.true; + + await utils.notifications.success.close(); }); }); @@ -79,6 +86,8 @@ describe('issues list', function() { await createIssueLightbox.waitClose(); expect(utils.notifications.success.open()).to.be.eventually.true; + + await utils.notifications.success.close(); }); }); diff --git a/e2e/suites/kanban.e2e.js b/e2e/suites/kanban.e2e.js index 2c7caf3d..549c75fa 100644 --- a/e2e/suites/kanban.e2e.js +++ b/e2e/suites/kanban.e2e.js @@ -44,10 +44,10 @@ describe('kanban', function() { createUSLightbox.subject().sendKeys(formFields.subject); // roles - createUSLightbox.setRole(1, 3); - createUSLightbox.setRole(2, 3); - createUSLightbox.setRole(3, 3); - createUSLightbox.setRole(4, 3); + await createUSLightbox.setRole(0, 3); + await createUSLightbox.setRole(1, 3); + await createUSLightbox.setRole(2, 3); + await createUSLightbox.setRole(3, 3); let totalPoints = await createUSLightbox.getRolePoints(); @@ -67,6 +67,12 @@ describe('kanban', function() { createUSLightbox.settings(1).click(); }); + it('upload attachments', commonHelper.lightboxAttachment); + + it('screenshots', function() { + utils.common.takeScreenshot('kanban', 'create-us-filled'); + }); + it('send form', async function() { createUSLightbox.submit(); @@ -111,10 +117,10 @@ describe('kanban', function() { subject.sendKeys(formFields.subject); // roles - createUSLightbox.setRole(1, 3); - createUSLightbox.setRole(2, 3); - createUSLightbox.setRole(3, 3); - createUSLightbox.setRole(4, 3); + await createUSLightbox.setRole(0, 3); + await createUSLightbox.setRole(1, 3); + await createUSLightbox.setRole(2, 3); + await createUSLightbox.setRole(3, 3); let totalPoints = await createUSLightbox.getRolePoints(); @@ -134,6 +140,8 @@ describe('kanban', function() { createUSLightbox.settings(1).click(); }); + it('upload attachments', commonHelper.lightboxAttachment); + it('send form', async function() { createUSLightbox.submit(); diff --git a/e2e/suites/project-home.e2e.js b/e2e/suites/project-home.e2e.js index dcaa0ec1..5afb2343 100644 --- a/e2e/suites/project-home.e2e.js +++ b/e2e/suites/project-home.e2e.js @@ -10,15 +10,15 @@ describe('project home', function() { beforeEach(async function() { browser.get(browser.params.glob.host + 'project/project-1/'); await utils.common.waitLoader(); + + await utils.common.takeScreenshot("project", "home-like"); + }); it('screenshot', async function() { await utils.common.takeScreenshot("project", "home"); }); - it('go to project', async function() { - await utils.common.goToFirstProject(); - }); /* it('timeline filled', function() { expect($$('div[tg-user-timeline-item]').count()).to.be.eventually.above(0); @@ -42,6 +42,18 @@ describe('project home', function() { }); */ it('unlike', async function() { + let reset = async function() { + //reset + let link = $('tg-like-project-button a'); + let likeActive = await utils.common.hasClass(link, 'active'); + + if (!likeActive) { + link.click(); + } + }; + + await reset(); + let link = $('tg-like-project-button a'); let likesCounterOld = parseInt(await link.$('.track-button-counter').getText(), 10); @@ -49,10 +61,10 @@ describe('project home', function() { await browser.waitForAngular(); - let likeActive = utils.common.hasClass(link, 'active'); + let likeActive = await utils.common.hasClass(link, 'active'); let likesCounter = parseInt(await link.$('.track-button-counter').getText(), 10); - expect(likeActive).to.be.eventually.false; + expect(likeActive).to.be.false; expect(likesCounter).to.be.equal(likesCounterOld - 1); }); @@ -65,10 +77,10 @@ describe('project home', function() { await browser.waitForAngular(); await utils.common.takeScreenshot("project", "home-like"); - let likeActive = utils.common.hasClass(link, 'active'); + let likeActive = await utils.common.hasClass(link, 'active'); let likesCounter = parseInt(await link.$('.track-button-counter').getText(), 10); - expect(likeActive).to.be.eventually.true; + expect(likeActive).to.be.true; expect(likesCounter).to.be.equal(likesCounterOld + 1); }); @@ -91,10 +103,10 @@ describe('project home', function() { return await utils.common.hasClass(watchOptions, 'hidden'); }, 4000); - let watchActive = utils.common.hasClass(link, 'active'); + let watchActive = await utils.common.hasClass(link, 'active'); let watchCounter = parseInt(await link.$('.track-button-counter').getText(), 10); - expect(watchActive).to.be.eventually.false; + expect(watchActive).to.be.false; expect(watchCounter).to.be.equal(watchCounterOld - 1); }); @@ -117,12 +129,12 @@ describe('project home', function() { return await utils.common.hasClass(watchOptions, 'hidden'); }, 4000); - let watchActive = utils.common.hasClass(link, 'active'); + let watchActive = await utils.common.hasClass(link, 'active'); let watchCounter = parseInt(await link.$('.track-button-counter').getText(), 10); await utils.common.takeScreenshot("project", "home-watch"); - expect(watchActive).to.be.eventually.true; + expect(watchActive).to.be.true; expect(watchCounter).to.be.equal(watchCounterOld + 1); }); diff --git a/e2e/suites/public/public.e2e.js b/e2e/suites/public/public.e2e.js index bfe04d31..c5f5118c 100644 --- a/e2e/suites/public/public.e2e.js +++ b/e2e/suites/public/public.e2e.js @@ -60,21 +60,25 @@ describe('Public', async function(){ utils.common.takeScreenshot('public', 'kanban'); }); - it('us detail', function() { + it('us detail', async function() { browser.get(browser.params.glob.host + 'project/project-3/backlog'); - utils.nav + await utils.common.waitLoader(); + + await utils.nav .init() .us(0) .go(); - utils.common.takeScreenshot('public', 'us-detail'); + return utils.common.takeScreenshot('public', 'us-detailb'); }); - it('issue detail', function() { + it('issue detail', async function() { browser.get(browser.params.glob.host + 'project/project-3/issues'); - utils.nav + await utils.common.waitLoader(); + + await utils.nav .init() .issue(0) .go(); @@ -85,7 +89,9 @@ describe('Public', async function(){ it('task detail', async function() { browser.get(browser.params.glob.host + 'project/project-3/backlog'); - utils.nav + await utils.common.waitLoader(); + + await utils.nav .init() .taskboard(0) .task(0) diff --git a/e2e/suites/tasks/taskboard.e2e.js b/e2e/suites/tasks/taskboard.e2e.js index ceac074a..1bfa66d6 100644 --- a/e2e/suites/tasks/taskboard.e2e.js +++ b/e2e/suites/tasks/taskboard.e2e.js @@ -252,23 +252,24 @@ describe('taskboard', function() { }); }); + describe ('inline', function() { + it('Change task assigned to', async function(){ + await taskboardHelper.watchersLinks().first().click(); - it('Change task assigned to', async function(){ - await taskboardHelper.watchersLinks().first().click(); + let lightbox = commonHelper.assignToLightbox(); - let lightbox = commonHelper.assignToLightbox(); + await lightbox.waitOpen(); - await lightbox.waitOpen(); + let assgnedToName = await lightbox.getName(0); - let assgnedToName = await lightbox.getName(0); + lightbox.selectFirst(); - lightbox.selectFirst(); + await lightbox.waitClose(); - await lightbox.waitClose(); + let usAssignedTo = await taskboardHelper.getBoxTasks(0, 0).get(0).$('.task-assigned').getText(); - let usAssignedTo = await taskboardHelper.getBoxTasks(0, 0).get(0).$('.task-assigned').getText(); - - expect(assgnedToName).to.be.equal(usAssignedTo); + expect(assgnedToName).to.be.equal(usAssignedTo); + }); }); describe('Graph', function(){ diff --git a/e2e/suites/user-profile/change-password.e2e.js b/e2e/suites/user-profile/change-password.e2e.js index 301ab3e4..6ba3247d 100644 --- a/e2e/suites/user-profile/change-password.e2e.js +++ b/e2e/suites/user-profile/change-password.e2e.js @@ -25,9 +25,11 @@ describe('change password', function() { await $('#new-password').sendKeys('123456'); await $('#retype-password').sendKeys('000'); - $('button[type="submit"]').click(); + $('.submit-button').click(); - expect(utils.notifications.error.open()).to.be.eventually.equal(true); + let waitErrorOpen = await utils.notifications.error.open(); + + expect(waitErrorOpen).to.be.ok; }); it('incorrect current password', async function() { @@ -37,7 +39,9 @@ describe('change password', function() { $('button[type="submit"]').click(); - expect(utils.notifications.error.open()).to.be.eventually.equal(true); + let waitErrorOpen = await utils.notifications.error.open(); + + expect(waitErrorOpen).to.be.ok; }); it('change password', async function() { @@ -47,7 +51,9 @@ describe('change password', function() { $('button[type="submit"]').click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let waitSuccessOpen = await utils.notifications.success.open(); + + expect(waitSuccessOpen).to.be.ok; }); after(async function() { @@ -61,6 +67,8 @@ describe('change password', function() { $('button[type="submit"]').click(); + await utils.notifications.success.open(); + await browser.waitForAngular(); - }) + }); }); diff --git a/e2e/suites/user-profile/edit-user-profile.e2e.js b/e2e/suites/user-profile/edit-user-profile.e2e.js index 3c22a3c2..cb601fc6 100644 --- a/e2e/suites/user-profile/edit-user-profile.e2e.js +++ b/e2e/suites/user-profile/edit-user-profile.e2e.js @@ -20,7 +20,9 @@ describe('edit user profile', function() { $('button[type="submit"]').click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let successOpen = await utils.notifications.success.open(); + + expect(successOpen).to.be.ok; // debounce :( await browser.sleep(2000); @@ -91,7 +93,7 @@ describe('edit user profile', function() { await htmlChanges(); - let avatar = imageContainer.$('.avatar'); + let avatar = imageContainer.$('.image'); let src = await avatar.getAttribute('src'); diff --git a/e2e/suites/user-profile/email-notification.e2e.js b/e2e/suites/user-profile/email-notification.e2e.js index d97620db..c84d3806 100644 --- a/e2e/suites/user-profile/email-notification.e2e.js +++ b/e2e/suites/user-profile/email-notification.e2e.js @@ -20,7 +20,9 @@ describe('email notification', function() { row.$$('label').get(0).click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let notificationOpen = await utils.notifications.success.open(); + + expect(notificationOpen).to.be.true; await utils.notifications.success.close(); }); @@ -30,7 +32,9 @@ describe('email notification', function() { row.$$('label').get(2).click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let notificationOpen = await utils.notifications.success.open(); + + expect(notificationOpen).to.be.true; await utils.notifications.success.close(); }); @@ -40,7 +44,9 @@ describe('email notification', function() { row.$$('label').get(1).click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let notificationOpen = await utils.notifications.success.open(); + + expect(notificationOpen).to.be.true; await utils.notifications.success.close(); }); diff --git a/e2e/suites/user-profile/feedback.e2e.js b/e2e/suites/user-profile/feedback.e2e.js index 0d71722c..3e297e6f 100644 --- a/e2e/suites/user-profile/feedback.e2e.js +++ b/e2e/suites/user-profile/feedback.e2e.js @@ -26,6 +26,8 @@ describe('feedback', function() { feedbackLightbox.$('button[type=submit]').click(); - expect(utils.notifications.success.open()).to.be.eventually.equal(true); + let notificationOpen = await utils.notifications.success.open(); + + expect(notificationOpen).to.be.true; }); }); diff --git a/e2e/suites/user-profile/user-profile-activity.e2e.js b/e2e/suites/user-profile/user-profile-activity.e2e.js index 3a21b4fa..191cd46a 100644 --- a/e2e/suites/user-profile/user-profile-activity.e2e.js +++ b/e2e/suites/user-profile/user-profile-activity.e2e.js @@ -53,7 +53,9 @@ describe('user profile - activity', function() { utils.common.takeScreenshot('user-profile', 'image-hover'); - expect(profileEdition.isDisplayed()).to.be.eventually.true; + let isDisplayed = await profileEdition.isDisplayed(); + + expect(isDisplayed).to.be.true; }); }); diff --git a/e2e/suites/user-profile/user-profile-contacts.e2e.js b/e2e/suites/user-profile/user-profile-contacts.e2e.js index d7c6e95e..0b608037 100644 --- a/e2e/suites/user-profile/user-profile-contacts.e2e.js +++ b/e2e/suites/user-profile/user-profile-contacts.e2e.js @@ -15,7 +15,7 @@ describe('user profile - contacts', function() { $$('.tab').get(4).click(); - browser.waitForAngular(); + await browser.waitForAngular(); utils.common.takeScreenshot('user-profile', 'current-user-contacts'); }); @@ -35,7 +35,7 @@ describe('user profile - contacts', function() { $$('.tab').get(5).click(); - browser.waitForAngular(); + await browser.waitForAngular(); utils.common.takeScreenshot('user-profile', 'other-user-contacts'); }); diff --git a/e2e/suites/user-profile/user-profile-projects.e2e.js b/e2e/suites/user-profile/user-profile-projects.e2e.js index c60e3009..9cff8f67 100644 --- a/e2e/suites/user-profile/user-profile-projects.e2e.js +++ b/e2e/suites/user-profile/user-profile-projects.e2e.js @@ -15,7 +15,7 @@ describe('user profile - projects', function() { $$('.tab').get(1).click(); - browser.waitForAngular(); + await browser.waitForAngular(); utils.common.takeScreenshot('user-profile', 'other-user-projects'); }); diff --git a/e2e/suites/user-profile/user-profile-votes.e2e.js b/e2e/suites/user-profile/user-profile-votes.e2e.js index f09c0f44..44ede43d 100644 --- a/e2e/suites/user-profile/user-profile-votes.e2e.js +++ b/e2e/suites/user-profile/user-profile-votes.e2e.js @@ -15,7 +15,7 @@ describe('user profile - votes', function() { $$('.tab').get(2).click(); - browser.waitForAngular(); + await browser.waitForAngular(); utils.common.takeScreenshot('user-profile', 'current-user-votes'); }); @@ -105,7 +105,7 @@ describe('user profile - votes', function() { $$('.tab').get(3).click(); - browser.waitForAngular(); + await browser.waitForAngular(); utils.common.takeScreenshot('user-profile', 'other-user-votes'); }); diff --git a/e2e/suites/user-stories/user-story-detail.e2e.js b/e2e/suites/user-stories/user-story-detail.e2e.js index 911f0013..e63d3294 100644 --- a/e2e/suites/user-stories/user-story-detail.e2e.js +++ b/e2e/suites/user-stories/user-story-detail.e2e.js @@ -32,7 +32,7 @@ describe('User story detail', function(){ it('description edition', sharedDetail.descriptionTesting); - it('status edition', sharedDetail.statusTesting); + it('status edition', sharedDetail.statusTesting.bind(this, 'Ready', 'In progress')); describe('assigned to edition', sharedDetail.assignedToTesting); @@ -80,33 +80,25 @@ describe('User story detail', function(){ it('create', async function() { let oldRelatedTaskCount = await usDetailHelper.relatedTasks().count(); - usDetailHelper.createRelatedTasks('test', 1, 1); + await usDetailHelper.createRelatedTasks('test', 1, 1); - expect(utils.notifications.success.open()).to.be.eventually.true; + let relatedTaskCount = await usDetailHelper.relatedTasks().count(); - utils.notifications.success.close(); - - let relatedTaskCount = usDetailHelper.relatedTasks().count(); - - expect(relatedTaskCount).to.be.eventually.equal(oldRelatedTaskCount + 1); + expect(relatedTaskCount).to.be.equal(oldRelatedTaskCount + 1); }); - it('edit', function() { - usDetailHelper.editRelatedTasks(0, 'test2', 2, 2); + it('edit', async function() { + await usDetailHelper.editRelatedTasks(0, 'test2', 2, 2); - expect(utils.notifications.success.open()).to.be.eventually.true; + let count = await usDetailHelper.editRelatedTasksEnabled(); - utils.notifications.success.close(); + expect(count).to.be.equal.false; }); it('delete', async function() { let oldRelatedTaskCount = await usDetailHelper.relatedTasks().count(); - usDetailHelper.deleteRelatedTask(0); - - expect(utils.notifications.success.open()).to.be.eventually.true; - - utils.notifications.success.close(); + await usDetailHelper.deleteRelatedTask(0); let relatedTaskCount = usDetailHelper.relatedTasks().count(); diff --git a/e2e/utils/common.js b/e2e/utils/common.js index 64009d45..2b041457 100644 --- a/e2e/utils/common.js +++ b/e2e/utils/common.js @@ -51,7 +51,7 @@ common.browserSkip = function(browserName, name, fn) { common.link = async function(el) { let oldUrl = await browser.getCurrentUrl(); - browser + await browser .actions() .mouseMove(el) .perform(); @@ -62,20 +62,21 @@ common.link = async function(el) { // aren't fired (we need them for the tg-nav calculation). Moving the cursor // "a little bit" tries to ensure the href text is really hovered and the // events are fired - browser.actions() + await browser.actions() .mouseMove({x: -10, y: -10}) .perform(); - browser.actions() + await browser.actions() .mouseMove({x: 10, y: 10}) .perform(); await browser.wait(async function() { let href = await el.getAttribute('href'); - return href.length > 1 && href !== browser.params.glob.host + "#"; + + return (href.length > 1 && href !== browser.params.glob.host + "#"); }, 5000); - browser + await browser .actions() .mouseMove(el) .click() @@ -83,6 +84,7 @@ common.link = async function(el) { return browser.wait(async function() { let newUrl = await browser.getCurrentUrl(); + return oldUrl !== newUrl; }, 5000); }; @@ -141,15 +143,21 @@ common.login = function(username, password) { let url = await browser.driver.getCurrentUrl(); return url === browser.params.glob.host; - }, 10000); + }, 10000).then(function() { + return common.closeJoyride(); + }); }; -common.logout = function() { - browser.actions() - .mouseMove($('div[tg-dropdown-user]')) +common.logout = async function() { + let dropdown = $('div[tg-dropdown-user]'); + + await browser.actions() + .mouseMove(dropdown) .perform(); - common.link($$('.navbar-dropdown li a').last()) + await common.waitTransitionTime(dropdown); + + $$('.navbar-dropdown li a').last().click(); return browser.driver.wait(async function() { let url = await browser.driver.getCurrentUrl(); @@ -161,7 +169,7 @@ common.prepare = function() { browser.get(browser.params.glob.host); return common.closeCookies(); -} +}; common.dragEnd = function(elm) { return browser.wait(async function() { diff --git a/e2e/utils/nav.js b/e2e/utils/nav.js index 65527210..107317d3 100644 --- a/e2e/utils/nav.js +++ b/e2e/utils/nav.js @@ -3,7 +3,7 @@ var helper = module.exports; var common = require('./common'); var actions = { - project: function(index) { + project: async function(index) { browser.actions().mouseMove($('div[tg-dropdown-project-list]')).perform(); let project = null; @@ -14,31 +14,31 @@ var actions = { project = $$('div[tg-dropdown-project-list] li a').get(index); } - common.link(project); + await common.link(project); return common.waitLoader(); }, - issues: function(index) { - common.link($('#nav-issues a')); + issues: async function(index) { + await common.link($('#nav-issues a')); return common.waitLoader(); }, - issue: function(index) { + issue: async function(index) { let issue = $$('section.issues-table .row.table-main .subject a').get(index); - common.link(issue); + await common.link(issue); return common.waitLoader(); }, - backlog: function() { - common.link($('#nav-backlog a')); + backlog: async function() { + await common.link($('#nav-backlog a')); return common.waitLoader(); }, - us: function(index) { + us: async function(index) { let us = $$('.user-story-name>a').get(index); - common.link(us); + await common.link(us); return common.waitLoader(); }, @@ -46,15 +46,17 @@ var actions = { browser.get(browser.params.glob.host); return common.waitLoader(); }, - taskboard: function(index) { + taskboard: async function(index) { let link = $$('.sprints .button-gray').get(index); - common.link(link); + await common.link(link); return common.waitLoader(); }, - task: function(index) { - common.link($$('div[tg-taskboard-task] a.task-name').get(index)); + task: async function(index) { + let task = $$('div[tg-taskboard-task] a.task-name').get(index); + + await common.link(task); return common.waitLoader(); } diff --git a/e2e/utils/popover.js b/e2e/utils/popover.js index cd55f284..e5a5991f 100644 --- a/e2e/utils/popover.js +++ b/e2e/utils/popover.js @@ -1,4 +1,4 @@ -var common = require('./common') +var common = require('./common'); var popover = module.exports; @@ -13,7 +13,7 @@ async function selectPopoverItem(popover, item) { popover.wait = async function() { await browser.wait(async function() { return await $$('.popover.active').count() === 1; - }, 1000); + }, 3000); return $('.popover.active'); }; diff --git a/gulpfile.js b/gulpfile.js index bb1848e8..bb3d9a71 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -173,7 +173,8 @@ paths.libs = [ paths.app + "js/jquery-ui.drag-multiple-custom.js", paths.app + "js/jquery.ui.touch-punch.min.js", paths.app + "js/tg-repeat.js", - paths.app + "js/sha1-custom.js" + paths.app + "js/sha1-custom.js", + paths.app + "js/murmurhash3_gc.js" ]; var isDeploy = argv["_"].indexOf("deploy") !== -1; @@ -438,7 +439,7 @@ gulp.task("jslibs-deploy", function() { .pipe(gulp.dest(paths.distVersion + "js/")); }); -gulp.task("app-watch", ["coffee-lint", "coffee", "conf", "locales", "app-loader"]); +gulp.task("app-watch", ["coffee", "conf", "locales", "app-loader"]); gulp.task("app-deploy", ["coffee", "conf", "locales", "app-loader"], function() { return gulp.src(paths.distVersion + "js/app.js") diff --git a/run-e2e.js b/run-e2e.js index 0c2ae066..33af48d8 100644 --- a/run-e2e.js +++ b/run-e2e.js @@ -17,7 +17,8 @@ var suites = [ 'kanban', 'projectHome', 'search', - 'team' + 'team', + 'discover' ]; var lunchSuites = [];