diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b75f68c..3edcdd55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Features - Ability to create url custom fields. (thanks to [@astagi](https://github.com/astagi)). - Moved from iconfont to SVG sprite icon system and redesign. +- Redesign 'Admin > Project > Modules' panel. - Add badge to project owners ### Misc diff --git a/app/coffee/modules/admin/project-profile.coffee b/app/coffee/modules/admin/project-profile.coffee index e28b1ef7..144970de 100644 --- a/app/coffee/modules/admin/project-profile.coffee +++ b/app/coffee/modules/admin/project-profile.coffee @@ -207,41 +207,51 @@ ProjectModulesDirective = ($repo, $confirm, $loading, projectService) -> link = ($scope, $el, $attrs) -> submit = => form = $el.find("form").checksley() + form.initializeFields() # Need to reset the form constrains + form.reset() # Need to reset the form constrains return if not form.validate() - target = angular.element(".admin-functionalities .submit-button") - currentLoading = $loading() - .target(target) - .start() - promise = $repo.save($scope.project) promise.then -> - currentLoading.finish() - $confirm.notify("success") $scope.$emit("project:loaded", $scope.project) + $confirm.notify("success") projectService.fetchProject() promise.then null, (data) -> - currentLoading.finish() - $confirm.notify("error", data._error_message) + form.setErrors(data) + if data._error_message + $confirm.notify("error", data._error_message) + + $el.on "change", ".module-activation.module-direct-active input", (event) -> + event.preventDefault() + submit() $el.on "submit", "form", (event) -> event.preventDefault() submit() - $el.on "click", ".admin-functionalities a.button-green", (event) -> + $el.on "click", ".icon-save", (event) -> event.preventDefault() submit() - $scope.$watch "isVideoconferenceActivated", (isVideoconferenceActivated) -> - if isVideoconferenceActivated - $el.find(".videoconference-attributes").removeClass("hidden") - else - $el.find(".videoconference-attributes").addClass("hidden") + $el.on "keydown", ".videoconference-attributes input", (e) -> + return e.which != 32 + + $scope.$watch "project.videoconferences", (newVal, oldVal) -> + # Reset videoconferences_extra_data if videoconference system change + if newVal? and oldVal? and newVal != oldVal + $scope.project.videoconferences_extra_data = "" + + $scope.$watch "isVideoconferenceActivated", (newValue, oldValue) -> + if newValue == false + # Reset videoconference attributes $scope.project.videoconferences = null $scope.project.videoconferences_extra_data = "" + # Save when videoconference is desactivated + submit() if oldValue == true + $scope.$watch "project", (project) -> if project.videoconferences? $scope.isVideoconferenceActivated = true diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index 11a0ec7a..34bbf115 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -430,6 +430,10 @@ "DISABLE": "Disable", "BACKLOG": "Backlog", "BACKLOG_DESCRIPTION": "Manage your user stories to maintain an organized view of upcoming and prioritized work.", + "NUMBER_SPRINTS": "Expected number of sprints", + "NUMBER_SPRINTS_HELP": "0 for an undetermined quantity", + "NUMBER_US_POINTS": "Expected total of story points", + "NUMBER_US_POINTS_HELP": "0 for an undetermined quantity", "KANBAN": "Kanban", "KANBAN_DESCRIPTION": "Organize your project in a lean way with this board.", "ISSUES": "Issues", @@ -437,9 +441,9 @@ "WIKI": "Wiki", "WIKI_DESCRIPTION": "Add, modify, or delete content in collaboration with others. This is the right place for your project documentation.", "MEETUP": "Meet Up", - "MEETUP_DESCRIPTION": "Choose your videoconference system. Even developers need face to face contact.", + "MEETUP_DESCRIPTION": "Choose your videoconference system.", "SELECT_VIDEOCONFERENCE": "Select a videoconference system", - "SALT_CHAT_ROOM": "If you want you can append a salt code to the name of the chat room", + "SALT_CHAT_ROOM": "Add a prefix to the chat room name", "JITSI_CHAT_ROOM": "Jitsi", "APPEARIN_CHAT_ROOM": "AppearIn", "TALKY_CHAT_ROOM": "Talky", @@ -451,8 +455,6 @@ "PROJECT_DETAILS": "Project details", "PROJECT_NAME": "Project name", "PROJECT_SLUG": "Project slug", - "NUMBER_SPRINTS": "Number of sprints (0 for an undetermined quantity)", - "NUMBER_US_POINTS": "Number of US points (0 for an undetermined quantity)", "TAGS": "Tags", "DESCRIPTION": "Description", "RECRUITING": "Is this project looking for people?", diff --git a/app/partials/admin/admin-project-modules.jade b/app/partials/admin/admin-project-modules.jade index fac254da..93ae5d6b 100644 --- a/app/partials/admin/admin-project-modules.jade +++ b/app/partials/admin/admin-project-modules.jade @@ -1,7 +1,10 @@ doctype html -div.wrapper(tg-project-modules, ng-controller="ProjectProfileController as ctrl", - ng-init="section='admin'; sectionName='ADMIN.MODULES.TITLE'") +div.wrapper( + tg-project-modules + ng-controller="ProjectProfileController as ctrl" + ng-init="section='admin'; sectionName='ADMIN.MODULES.TITLE'" +) tg-project-menu sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-profile") include ../includes/modules/admin-menu @@ -13,94 +16,159 @@ div.wrapper(tg-project-modules, ng-controller="ProjectProfileController as ctrl" header include ../includes/components/mainTitle - form - div.functionality(ng-class="{true:'active', false:''}[project.is_backlog_activated]") - svg.icon.icon-scrum - use(xlink:href="#icon-scrum") - div.desc - p - span.title(translate="ADMIN.MODULES.BACKLOG") - span(translate="ADMIN.MODULES.BACKLOG_DESCRIPTION") - div.activate - input.activate-input(type="checkbox", id="functionality-backlog", - ng-model="project.is_backlog_activated") - label.button.button-gray(ng-switch="project.is_backlog_activated", - for="functionality-backlog") - span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE") - span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE") + form.module-container + .module.module-scrum(ng-class="{true:'active', false:''}[project.is_backlog_activated]") + .module-icon + svg.icon.icon-scrum + use(xlink:href="#icon-scrum") + .module-name(translate="ADMIN.MODULES.BACKLOG") + .module-desc + p(translate="ADMIN.MODULES.BACKLOG_DESCRIPTION") + .module-desc-options(ng-if="project.is_backlog_activated") + fieldset + label(for="total-sprints") {{ 'ADMIN.MODULES.NUMBER_SPRINTS' | translate }} + input( + id="total-sprints" + name="total-sprints" + type="number" + min="0" + placeholder="{{'ADMIN.MODULES.NUMBER_SPRINTS_HELP' | translate}}" + ng-model="project.total_milestones" + data-type="digits" + ) - div.functionality(ng-class="{true:'active', false:''}[project.is_kanban_activated]") - svg.icon.icon-kanban - use(xlink:href="#icon-kanban") - div.desc - p - span.title(translate="ADMIN.MODULES.KANBAN") - span(translate="ADMIN.MODULES.KANBAN_DESCRIPTION") - div.activate - input.activate-input(type="checkbox", id="functionality-kanban", - ng-model="project.is_kanban_activated") - label.button.button-gray(ng-switch="project.is_kanban_activated", - for="functionality-kanban") - span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE") - span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE") + fieldset + label(for="total-story-points") {{ 'ADMIN.MODULES.NUMBER_US_POINTS' | translate }} + input( + id="total-story-points" + name="total-story-points" + type="number" + min="0" + placeholder="{{'ADMIN.MODULES.NUMBER_US_POINTS_HELP' | translate}}" + ng-model="project.total_story_points" + data-type="digits" + ) + svg.icon.icon-save(ng-if="project.is_backlog_activated") + use(xlink:href="#icon-save") + .module-activation.module-direct-active + div.check + input.activate-input( + id="functionality-backlog" + name="functionality-backlog" + type="checkbox" + ng-checked="project.is_backlog_activated" + ng-model="project.is_backlog_activated" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") - div.functionality(ng-class="{true:'active', false:''}[project.is_issues_activated]") - svg.icon.icon-issues - use(xlink:href="#icon-issues") - div.desc - p - span.title(translate="ADMIN.MODULES.ISSUES") - span(translate="ADMIN.MODULES.ISSUES_DESCRIPTION") - div.activate - input.activate-input(type="checkbox", id="functionality-issues", - ng-model="project.is_issues_activated") - label.button.button-gray(ng-switch="project.is_issues_activated", - for="functionality-issues") - span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE") - span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE") + .module.module-kanban(ng-class="{true:'active', false:''}[project.is_kanban_activated]") + .module-icon + svg.icon.icon-kanban + use(xlink:href="#icon-kanban") + .module-name(translate="ADMIN.MODULES.KANBAN") + .module-desc(translate="ADMIN.MODULES.KANBAN_DESCRIPTION") + .module-activation.module-direct-active + div.check + input.activate-input( + id="functionality-kanban" + name="functionality-kanban" + type="checkbox" + ng-checked="project.is_kanban_activated" + ng-model="project.is_kanban_activated" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") - div.functionality(ng-class="{true:'active', false:''}[project.is_wiki_activated]") - svg.icon.icon-wiki - use(xlink:href="#icon-wiki") - div.desc - p - span.title(translate="ADMIN.MODULES.WIKI") - span(translate="ADMIN.MODULES.WIKI_DESCRIPTION") - div.activate - input.activate-input(type="checkbox", id="functionality-wiki", - ng-model="project.is_wiki_activated") - label.button.button-gray(ng-switch="project.is_wiki_activated", - for="functionality-wiki") - span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE") - span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE") + .module.module-issues(ng-class="{true:'active', false:''}[project.is_issues_activated]") + .module-icon + svg.icon.icon-issues + use(xlink:href="#icon-issues") + .module-name(translate="ADMIN.MODULES.ISSUES") + .module-desc(translate="ADMIN.MODULES.ISSUES_DESCRIPTION") + .module-activation.module-direct-active + div.check + input.activate-input( + id="functionality-issues" + name="functionality-issues" + type="checkbox" + ng-checked="project.is_issues_activated" + ng-model="project.is_issues_activated" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") - div.functionality(ng-class="{true:'active', false:''}[isVideoconferenceActivated]") - svg.icon.icon-bubble-empty - use(xlink:href="#icon-bubble-empty") - div.desc - p - span.title(translate="ADMIN.MODULES.MEETUP") - span(translate="ADMIN.MODULES.MEETUP_DESCRIPTION") - div.activate - input.activate-input(type="checkbox", id="functionality-video", - ng-model="isVideoconferenceActivated") - label.button.button-gray(ng-switch="isVideoconferenceActivated", - for="functionality-video") - span(ng-switch-when="true", translate="ADMIN.MODULES.DISABLE") - span(ng-switch-when="false", translate="ADMIN.MODULES.ENABLE") + .module.module-wiki(ng-class="{true:'active', false:''}[project.is_wiki_activated]") + .module-icon + svg.icon.icon-wiki + use(xlink:href="#icon-wiki") + .module-name(translate="ADMIN.MODULES.WIKI") + .module-desc(translate="ADMIN.MODULES.WIKI_DESCRIPTION") + .module-activation.module-direct-active + div.check + input.activate-input( + id="functionality-wiki" + name="functionality-wiki" + type="checkbox" + ng-checked="project.is_wiki_activated" + ng-model="project.is_wiki_activated" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") - div.videoconference-attributes.hidden - select(ng-model="project.videoconferences", - ng-options="e.id as e.name|translate for e in [{'id':'appear-in', 'name':'ADMIN.MODULES.APPEARIN_CHAT_ROOM'},{'id':'jitsi', 'name': 'ADMIN.MODULES.JITSI_CHAT_ROOM'},{'id':'talky', 'name': 'ADMIN.MODULES.TALKY_CHAT_ROOM'},{'id':'custom', 'name': 'ADMIN.MODULES.CUSTOM_CHAT_ROOM'}]") - option(value="", translate="ADMIN.MODULES.SELECT_VIDEOCONFERENCE") - input(ng-if="project.videoconferences && project.videoconferences != 'custom'", - type="text", - ng-model="project.videoconferences_extra_data", - data-maxlength="255", - placeholder="{{'ADMIN.MODULES.SALT_CHAT_ROOM' | translate}}") - input(ng-if="project.videoconferences == 'custom'", - type="text", - ng-model="project.videoconferences_extra_data", - data-maxlength="255", - placeholder="{{'ADMIN.MODULES.URL_CHAT_ROOM' | translate}}") - button.button-green.submit-button(type="submit", title="{{'COMMON.SAVE' | translate}}", translate="COMMON.SAVE") + .module.module-videoconference(ng-class="{true:'active', false:''}[isVideoconferenceActivated]") + .module-icon + svg.icon.icon-bubble-empty + use(xlink:href="#icon-bubble-empty") + .module-name(translate="ADMIN.MODULES.MEETUP") + .module-desc + p(translate="ADMIN.MODULES.MEETUP_DESCRIPTION") + div.videoconference-attributes(ng-if="isVideoconferenceActivated") + select( + id="videoconference-type" + name="videoconference-type" + ng-model="project.videoconferences" + ng-options="e.id as e.name|translate for e in [{'id':'appear-in', 'name':'ADMIN.MODULES.APPEARIN_CHAT_ROOM'},{'id':'jitsi', 'name': 'ADMIN.MODULES.JITSI_CHAT_ROOM'},{'id':'talky', 'name': 'ADMIN.MODULES.TALKY_CHAT_ROOM'},{'id':'custom', 'name': 'ADMIN.MODULES.CUSTOM_CHAT_ROOM'}]") + option( + value="" + translate="ADMIN.MODULES.SELECT_VIDEOCONFERENCE" + ) + fieldset(ng-if="project.videoconferences && project.videoconferences != 'custom'") + input( + id="videoconference-prefix" + name="videoconference-prefix" + type="text" + ng-model="project.videoconferences_extra_data" + data-maxlength="250" + placeholder="{{'ADMIN.MODULES.SALT_CHAT_ROOM' | translate}}" + ) + fieldset(ng-if="project.videoconferences == 'custom'") + input( + id="videoconference-url" + name="videoconference-url" + type="url" + ng-model="project.videoconferences_extra_data" + data-maxlength="250" + placeholder="{{'ADMIN.MODULES.URL_CHAT_ROOM' | translate}}" + data-type="url" + data-required="true" + ) + svg.icon.icon-save(ng-if="project.videoconferences") + use(xlink:href="#icon-save") + + .module-activation + div.check + input.activate-input( + id="functionality-video" + name="functionality-video" + type="checkbox" + ng-checked="project.isVideoconferenceActivated" + ng-model="isVideoconferenceActivated" + ) + div + span.check-text.check-yes(translate="COMMON.YES") + span.check-text.check-no(translate="COMMON.NO") diff --git a/app/partials/admin/admin-project-profile.jade b/app/partials/admin/admin-project-profile.jade index bb5e3b2e..229739af 100644 --- a/app/partials/admin/admin-project-profile.jade +++ b/app/partials/admin/admin-project-profile.jade @@ -78,30 +78,6 @@ div.wrapper( ng-model="project.tags" ) - fieldset - label(for="project-sprints") {{ 'ADMIN.PROJECT_PROFILE.NUMBER_SPRINTS' | translate }} - 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" - ) - - fieldset - label(for="total-story-points") {{ 'ADMIN.PROJECT_PROFILE.NUMBER_US_POINTS' | translate }} - 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" - ) - fieldset.looking-for-people .looking-for-people-selector span {{ 'ADMIN.PROJECT_PROFILE.RECRUITING' | translate }} diff --git a/app/partials/backlog/backlog.jade b/app/partials/backlog/backlog.jade index 046fefb2..11556f1a 100644 --- a/app/partials/backlog/backlog.jade +++ b/app/partials/backlog/backlog.jade @@ -16,7 +16,7 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl", use(xlink:href="#icon-graph") div.empty-text p.title(translate="BACKLOG.CUSTOMIZE_GRAPH") - p {{'BACKLOG.CUSTOMIZE_GRAPH_TEXT' | translate}} #[a(href="", tg-nav="project-admin-project-profile-details:project=project.slug", title="{{'BACKLOG.CUSTOMIZE_GRAPH_TITLE' | translate}}") {{'BACKLOG.CUSTOMIZE_GRAPH_ADMIN' | translate}}] + p {{'BACKLOG.CUSTOMIZE_GRAPH_TEXT' | translate}} #[a(href="", tg-nav="project-admin-project-profile-modules:project=project.slug", title="{{'BACKLOG.CUSTOMIZE_GRAPH_TITLE' | translate}}") {{'BACKLOG.CUSTOMIZE_GRAPH_ADMIN' | translate}}] div.graphics-container.js-burndown-graph div.burndown(tg-burndown-backlog-graph) diff --git a/app/styles/components/check.scss b/app/styles/components/check.scss index 1cada375..176b1b2e 100644 --- a/app/styles/components/check.scss +++ b/app/styles/components/check.scss @@ -8,12 +8,12 @@ width: 65px; input { cursor: pointer; - height: 500px; + height: 50px; left: -10px; opacity: 0; position: absolute; top: -10px; - width: 500px; + width: 100px; z-index: 999; + div { background-color: $gray; diff --git a/app/styles/modules/admin/admin-functionalities.scss b/app/styles/modules/admin/admin-functionalities.scss index e928d937..0dacc074 100644 --- a/app/styles/modules/admin/admin-functionalities.scss +++ b/app/styles/modules/admin/admin-functionalities.scss @@ -1,62 +1,75 @@ .admin-functionalities { - form { - display: flex; - flex-wrap: wrap; + .module-container { + max-width: 900px; + width: 100%; } - .functionality { + .module { align-content: center; - align-items: center; - background-color: $whitish; + align-items: flex-start; + border-bottom: 1px solid $whitish; display: flex; - flex-direction: column; - justify-content: center; - margin-bottom: .3rem; - margin-right: .3rem; - opacity: .5; - padding: 1rem; - position: relative; - transition: all .2s linear; - vertical-align: top; - width: 32%; + padding: 1rem 0; &.active { - background-color: rgba($primary, .3); - opacity: 1; - } - .icon { - fill: $gray; - height: 3rem; - margin: 1rem auto; - width: 3rem; - } - .desc { - text-align: center; - width: 100%; - } - .activate-input { - display: none; - +label { - color: $white; - cursor: pointer; - display: block; - text-align: center; + .module-icon .icon, + .module-name { + color: $primary; + fill: $primary; } } - .title { - @extend %bold; - display: block; - } - select { - margin-top: 1rem; + } + .module-icon { + flex-basis: 2rem; + flex-shrink: 0; + margin: 0 .5rem 0 0; + .icon { + @include svg-size(3rem); + fill: $gray-light; } } + .module-name { + @extend %bold; + @extend %large; + color: $gray-light; + flex-basis: 100px; + flex-shrink: 0; + margin: 0 .5rem; + } + .module-desc { + @extend %small; + color: $gray-light; + flex: 1; + margin: 0 2rem 0 0; + p { + margin: 0; + } + } + .module-desc-options, .videoconference-attributes { - select { - margin-bottom: .5rem; + align-items: flex-start; + display: flex; + margin-top: .5rem; + fieldset, + .icon { + margin: 0 .5rem; + } + .icon { + @include svg-size(2.5rem); + align-self: center; + cursor: pointer; + fill: $gray-light; + &:hover { + fill: $primary; + } } } - .button-green { - color: $white; - display: block; - text-align: center; + .module-scrum { + .icon { + align-self: flex-end; + } + } + .module-videoconference { + .icon { + align-self: flex-start; + } } }