spent time as link; fixes

pull/2687/head
Mihail Maxacov 10 years ago
parent e4abbf3e7e
commit cc7bb9bcd2
  1. 1
      frontend/app/templates/components/inplace_editor/display/spent_time.html
  2. 6
      frontend/app/templates/components/inplace_editor/editable/boolean.html
  3. 8
      frontend/app/templates/work_packages/field.html
  4. 10
      frontend/app/templates/work_packages/inplace_editor/custom/display/spent_time.html
  5. 7
      frontend/app/templates/work_packages/inplace_editor/display_pane.html
  6. 6
      frontend/app/templates/work_packages/tabs/overview.html
  7. 5
      frontend/app/work_packages/controllers/details-tab-overview-controller.js
  8. 1
      frontend/app/work_packages/directives/inplace_editor/custom/display/index.js
  9. 52
      frontend/app/work_packages/directives/inplace_editor/custom/display/inplace-display-spent-time-directive.js
  10. 9
      frontend/app/work_packages/directives/inplace_editor/custom/display/inplace-display-version-directive.js
  11. 6
      frontend/app/work_packages/directives/inplace_editor/inplace-editor-display-pane-directive.js
  12. 11
      frontend/app/work_packages/directives/inplace_editor/inplace-editor-edit-pane-directive.js
  13. 12
      frontend/app/work_packages/directives/work-package-field-directive.js
  14. 58
      frontend/app/work_packages/services/work-package-field-service.js

@ -0,0 +1 @@
<inplace-display-spent-time></inplace-display-spent-time>

@ -1,10 +1,10 @@
<div class="switch"> <div class="switch">
<input type="checkbox" <input type="checkbox"
class="focus-input" class="focus-input"
id="{{ fieldController.checkboxId }}" id="checkbox-switch-{{ fieldController.field }}"
name="value" name="value"
ng-disabled="isBusy" ng-disabled="isBusy"
title="{{ editTitle }}" title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" /> ng-model="fieldController.writeValue" />
<label for="{{ fieldController.checkboxId }}" title="{{ fieldController.editTitle }}"></label> <label for="checkbox-switch-{{ fieldController.field }}" title="{{ fieldController.editTitle }}"></label>
</div> </div>

@ -1,12 +1,6 @@
<div class="work-package-field"> <div class="work-package-field">
<div class="editable-branch" ng-if="fieldController.isEditable()">
<inplace-editor-main-pane> <inplace-editor-main-pane>
<inplace-editor-display-pane></inplace-editor-display-pane> <inplace-editor-display-pane></inplace-editor-display-pane>
<inplace-editor-edit-pane></inplace-editor-edit-pane> <inplace-editor-edit-pane ng-if="fieldController.isEditable()"></inplace-editor-edit-pane>
</inplace-editor-main-pane> </inplace-editor-main-pane>
</div> </div>
<div class="static-branch" ng-if="!fieldController.isEditable()">
<empty-element ng-if="fieldController.isEmpty()"></empty-element>
<div class="field-value" ng-if="!fieldController.isEmpty()" ng-bind="fieldController.value"></div>
</div>
</div>

@ -0,0 +1,10 @@
<div class="spent-time-wrapper">
<span ng-if="customEditorController.isLinkViewable()">
<a href="{{ customEditorController.getPath() }}">
{{ displayPaneController.getReadValue() }}
</a>
</span>
<span ng-if="!customEditorController.isLinkViewable()">
{{ displayPaneController.getReadValue() }}
</span>
</div>

@ -1,6 +1,7 @@
<div class="inplace-edit--read" ng-hide="fieldController.isEditing" ng-click="displayPaneController.startEditing()"> <div class="inplace-edit--read" ng-hide="fieldController.isEditing">
<span class="editing-link-wrapper">
<accessible-by-keyboard <accessible-by-keyboard
ng-if="fieldController.isEditable()"
ng-click="displayPaneController.startEditing()"
class="inplace-editing--trigger-container" class="inplace-editing--trigger-container"
span-class="inplace-editing--container" span-class="inplace-editing--container"
link-class="inplace-editing--trigger-link" link-class="inplace-editing--trigger-link"
@ -11,5 +12,7 @@
</icon-wrapper> </icon-wrapper>
</span> </span>
</accessible-by-keyboard> </accessible-by-keyboard>
<span ng-if="!fieldController.isEditable()">
<span class="inplace-edit--read-value" ng-include="templateUrl"></span>
</span> </span>
</div> </div>

@ -29,9 +29,9 @@
</div> </div>
</div> </div>
<dl class="attributes-key-value" ng-repeat="field in group.attributes"> <dl class="attributes-key-value">
<dt class="attributes-key-value--key" ng-bind="vm.getLabel(field)"></dt> <dt ng-if="vm.isSpecified(field)" ng-repeat-start="field in group.attributes" class="attributes-key-value--key" ng-bind="vm.getLabel(field)"></dt>
<dd class="attributes-key-value--value-container"> <dd ng-if="vm.isSpecified(field)" ng-repeat-end class="attributes-key-value--value-container">
<work-package-field field="field"></work-package-field> <work-package-field field="field"></work-package-field>
</dd> </dd>
</dl> </dl>

@ -40,6 +40,7 @@ module.exports = function(
vm.isGroupEmpty = isGroupEmpty; vm.isGroupEmpty = isGroupEmpty;
vm.getLabel = getLabel; vm.getLabel = getLabel;
vm.isSpecified = isSpecified;
vm.showToggleButton = showToggleButton; vm.showToggleButton = showToggleButton;
activate(); activate();
@ -63,6 +64,10 @@ module.exports = function(
}); });
} }
function isSpecified(field) {
return WorkPackageFieldService.isSpecified(vm.workPackage, field);
}
function getLabel(field) { function getLabel(field) {
return WorkPackageFieldService.getLabel(vm.workPackage, field); return WorkPackageFieldService.getLabel(vm.workPackage, field);
} }

@ -28,4 +28,5 @@
angular.module('openproject.workPackages.directives') angular.module('openproject.workPackages.directives')
.directive('inplaceDisplayUser', require('./inplace-display-user-directive')) .directive('inplaceDisplayUser', require('./inplace-display-user-directive'))
.directive('inplaceDisplaySpentTime', require('./inplace-display-spent-time-directive'))
.directive('inplaceDisplayVersion', require('./inplace-display-version-directive')); .directive('inplaceDisplayVersion', require('./inplace-display-version-directive'));

@ -0,0 +1,52 @@
//-- copyright
// OpenProject is a project management system.
// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 3.
//
// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
// Copyright (C) 2006-2013 Jean-Philippe Lang
// Copyright (C) 2010-2013 the ChiliProject Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function(EditableFieldsState) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^inplaceEditorDisplayPane',
templateUrl: '/templates/work_packages/inplace_editor/custom/display/spent_time.html',
controller: function($scope) {
this.isLinkViewable = function() {
return EditableFieldsState.workPackage.links.timeEntries;
};
this.getPath = function() {
return EditableFieldsState.workPackage.links.timeEntries.href;
}
},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, displayPaneController) {
scope.displayPaneController = displayPaneController;
}
};
};

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details. // See doc/COPYRIGHT.rdoc for more details.
//++ //++
module.exports = function(EditableFieldsState, PathHelper, VersionService) { module.exports = function(EditableFieldsState, PathHelper, VersionService, $timeout) {
return { return {
restrict: 'E', restrict: 'E',
transclude: true, transclude: true,
@ -46,6 +46,13 @@ module.exports = function(EditableFieldsState, PathHelper, VersionService) {
scope.customEditorController.version = version; scope.customEditorController.version = version;
VersionService.isVersionFieldViewable(EditableFieldsState.workPackage, displayPaneController.field).then(function(isViewable) { VersionService.isVersionFieldViewable(EditableFieldsState.workPackage, displayPaneController.field).then(function(isViewable) {
scope.customEditorController.isVersionFieldViewable = isViewable; scope.customEditorController.isVersionFieldViewable = isViewable;
// need to reset the click listener due to async resolution
// of link visibility
$timeout(function() {
element.find('a').off('click').on('click', function(e) {
e.stopPropagation();
});
});
}); });
}); });
} }

@ -71,8 +71,12 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, $timeout
}); });
$timeout(function() { $timeout(function() {
//element.on('click', 'a a', function(e) {
// console.log(e, 'adasd');
// e.stopPropagation();
// return false;
//});
element.find('a').on('click', function(e) { element.find('a').on('click', function(e) {
console.log('a clicnk');
e.stopPropagation(); e.stopPropagation();
}); });
}); });

@ -39,7 +39,6 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, FocusHel
var fieldController = $scope.fieldController; var fieldController = $scope.fieldController;
fieldController.isBusy = true; fieldController.isBusy = true;
var pendingFormChanges = getPendingFormChanges(); var pendingFormChanges = getPendingFormChanges();
console.log(fieldController.writeValue, 'wv');
pendingFormChanges[fieldController.field] = fieldController.writeValue; pendingFormChanges[fieldController.field] = fieldController.writeValue;
var result = WorkPackageService.updateWorkPackage(EditableFieldsState.workPackage, notify); var result = WorkPackageService.updateWorkPackage(EditableFieldsState.workPackage, notify);
@ -60,10 +59,12 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, FocusHel
this.discardEditing = function() { this.discardEditing = function() {
$scope.fieldController.isEditing = false; $scope.fieldController.isEditing = false;
var form = EditableFieldsState.workPackage.form; var form = EditableFieldsState.workPackage.form;
getPendingFormChanges()[$scope.fieldController.field] = EditableFieldsState.workPackage.form.embedded.payload.props[$scope.fieldController.field]; delete getPendingFormChanges()[$scope.fieldController.field];
$scope.fieldController.updateWriteValue(); $scope.fieldController.updateWriteValue();
}; };
this.getPendingFormChanges = getPendingFormChanges;
function getPendingFormChanges() { function getPendingFormChanges() {
var form = EditableFieldsState.workPackage.form; var form = EditableFieldsState.workPackage.form;
form.pendingChanges = form.pendingChanges || angular.copy(form.embedded.payload.props); form.pendingChanges = form.pendingChanges || angular.copy(form.embedded.payload.props);
@ -95,6 +96,12 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, FocusHel
} }
}); });
scope.$watch('fieldController.writeValue', function(writeValue) {
if (scope.fieldController.isEditing) {
scope.editPaneController.getPendingFormChanges()[scope.fieldController.field] = writeValue;
}
}, true);
scope.$watch('fieldController.isEditing', function(isEditing) { scope.$watch('fieldController.isEditing', function(isEditing) {
if (isEditing) { if (isEditing) {
scope.editPaneController.error = null; scope.editPaneController.error = null;

@ -29,8 +29,9 @@
module.exports = function(WorkPackageFieldService, EditableFieldsState) { module.exports = function(WorkPackageFieldService, EditableFieldsState) {
function workPackageFieldDirectiveController() { function workPackageFieldDirectiveController() {
this.isEditable = function() { this.isEditable = function() {
return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field) return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field);
}; };
this.isEmpty = function() { this.isEmpty = function() {
@ -46,19 +47,10 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState) {
}; };
if (this.isEditable()) { if (this.isEditable()) {
this.type = 'text';
this.isBusy = false; this.isBusy = false;
this.isEditing = false; this.isEditing = false;
this.updateWriteValue(); this.updateWriteValue();
this.editTitle = I18n.t('js.inplace.button_edit', { attribute: this.field }); this.editTitle = I18n.t('js.inplace.button_edit', { attribute: this.field });
} else {
var value = WorkPackageFieldService.format(EditableFieldsState.workPackage, this.field);
if (value) {
// until properties are set as non-editable when there are no available values
this.value = value.props ? value.props.name : value;
} else {
this.value = value;
}
} }
} }

@ -29,6 +29,10 @@
module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackagesHelper, $q, $http) { module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackagesHelper, $q, $http) {
function isEditable(workPackage, field) { function isEditable(workPackage, field) {
// no form - no editing
if (!workPackage.form) {
return false;
}
// TODO: extract to strategy if new cases arise // TODO: extract to strategy if new cases arise
if (field === 'date') { if (field === 'date') {
// nope // nope
@ -44,7 +48,7 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
var isWritable = workPackage.schema.props[field].writable; var isWritable = workPackage.schema.props[field].writable;
// not writable if no embedded allowed values // not writable if no embedded allowed values
if (workPackage.form.embedded.schema if (workPackage.form && workPackage.form.embedded.schema
.props[field]._links && allowedValuesEmbedded(workPackage, field)) { .props[field]._links && allowedValuesEmbedded(workPackage, field)) {
if (getEmbeddedAllowedValues(workPackage, field).length === 0) { if (getEmbeddedAllowedValues(workPackage, field).length === 0) {
return false; return false;
@ -53,6 +57,15 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
return isWritable; return isWritable;
} }
function isSpecified(workPackage, field) {
if (field === 'date') {
// kind of specified
return true;
}
return !_.isUndefined(workPackage.schema
.props[field]);
}
function getValue(workPackage, field) { function getValue(workPackage, field) {
if (!_.isUndefined(workPackage.props[field])) { if (!_.isUndefined(workPackage.props[field])) {
return workPackage.props[field]; return workPackage.props[field];
@ -132,6 +145,9 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
} }
function isEmpty(workPackage, field) { function isEmpty(workPackage, field) {
if (field === 'date') {
return getValue(workPackage, 'startDate') === null && getValue(workPackage, 'dueDate') === null;
}
var value = WorkPackageFieldService.getValue(workPackage, field); var value = WorkPackageFieldService.getValue(workPackage, field);
return value === null || value === ''; return value === null || value === '';
} }
@ -180,17 +196,23 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
displayStrategy = 'embedded'; displayStrategy = 'embedded';
if (field === 'date') { if (field === 'date') {
fieldType = 'DateRange'; fieldType = 'DateRange';
} else if (field === 'spentTime') {
fieldType = 'SpentTime';
} else { } else {
fieldType = workPackage.form.embedded.schema.props[field].type; fieldType = workPackage.schema.props[field].type;
} }
switch(fieldType) { switch(fieldType) {
case 'String': case 'String':
case 'Integer': case 'Integer':
case 'Float': case 'Float':
case 'Duration':
case 'DateRange':
case 'Date':
case 'Boolean':
displayStrategy = 'text'; displayStrategy = 'text';
break; break;
case 'Boolean': case 'SpentTime':
displayStrategy = 'boolean'; displayStrategy = 'spent_time';
break; break;
case 'Formattable': case 'Formattable':
displayStrategy = 'wiki_textarea'; displayStrategy = 'wiki_textarea';
@ -207,10 +229,22 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
} }
function format(workPackage, field) { function format(workPackage, field) {
if (field === 'date') {
var displayedStartDate = WorkPackagesHelper.formatValue(workPackage.props.startDate, 'startDate') || I18n.t('js.label_no_start_date'),
displayedEndDate = WorkPackagesHelper.formatValue(workPackage.props.dueDate, 'dueDate') || I18n.t('js.label_no_due_date');
return displayedStartDate + ' - ' + displayedEndDate;
}
var value = workPackage.props[field]; var value = workPackage.props[field];
if (_.isUndefined(value)) { if (_.isUndefined(value)) {
// might be embedded
return WorkPackageFieldService.getValue(workPackage, field); return WorkPackageFieldService.getValue(workPackage, field);
} }
if (value === null) {
return null;
}
var mappings = { var mappings = {
dueDate: 'date', dueDate: 'date',
startDate: 'date', startDate: 'date',
@ -218,21 +252,23 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
updatedAt: 'datetime' updatedAt: 'datetime'
}; };
// TODO: switch to duration if (workPackage.schema.props[field].type === 'Duration') {
if (field === 'estimatedTime' || field === 'spentTime') {
if (value === null) {
return null;
}
var hours = moment.duration(value).asHours(); var hours = moment.duration(value).asHours();
return I18n.t('js.units.hour', { count: hours.toFixed(2) }); return I18n.t('js.units.hour', { count: hours.toFixed(2) });
} else {
return WorkPackagesHelper.formatValue(value, mappings[field]);
} }
if (workPackage.schema.props[field].type === 'Boolean') {
return value ? I18n.t('js.general_text_yes') : I18n.t('js.general_text_no');
}
return WorkPackagesHelper.formatValue(value, mappings[field]);
} }
var WorkPackageFieldService = { var WorkPackageFieldService = {
isEditable: isEditable, isEditable: isEditable,
isRequired: isRequired, isRequired: isRequired,
isSpecified: isSpecified,
isEmpty: isEmpty, isEmpty: isEmpty,
isEmbedded: isEmbedded, isEmbedded: isEmbedded,
isSavedAsLink: isSavedAsLink, isSavedAsLink: isSavedAsLink,

Loading…
Cancel
Save