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. 2
      frontend/app/templates/work_packages/inplace_editor/custom/display/version.html
  6. 9
      frontend/app/templates/work_packages/inplace_editor/display_pane.html
  7. 6
      frontend/app/templates/work_packages/tabs/overview.html
  8. 5
      frontend/app/work_packages/controllers/details-tab-overview-controller.js
  9. 1
      frontend/app/work_packages/directives/inplace_editor/custom/display/index.js
  10. 52
      frontend/app/work_packages/directives/inplace_editor/custom/display/inplace-display-spent-time-directive.js
  11. 9
      frontend/app/work_packages/directives/inplace_editor/custom/display/inplace-display-version-directive.js
  12. 6
      frontend/app/work_packages/directives/inplace_editor/inplace-editor-display-pane-directive.js
  13. 11
      frontend/app/work_packages/directives/inplace_editor/inplace-editor-edit-pane-directive.js
  14. 12
      frontend/app/work_packages/directives/work-package-field-directive.js
  15. 60
      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">
<input type="checkbox"
class="focus-input"
id="{{ fieldController.checkboxId }}"
id="checkbox-switch-{{ fieldController.field }}"
name="value"
ng-disabled="isBusy"
title="{{ editTitle }}"
title="{{ fieldController.editTitle }}"
ng-model="fieldController.writeValue" />
<label for="{{ fieldController.checkboxId }}" title="{{ fieldController.editTitle }}"></label>
<label for="checkbox-switch-{{ fieldController.field }}" title="{{ fieldController.editTitle }}"></label>
</div>

@ -1,12 +1,6 @@
<div class="work-package-field">
<div class="editable-branch" ng-if="fieldController.isEditable()">
<inplace-editor-main-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>
</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>

@ -2,7 +2,7 @@
<span ng-if="!customEditorController.version">-</span>
<span ng-if="customEditorController.version && customEditorController.isVersionFieldViewable">
<a href="{{customEditorController.pathHelper.versionPath(customEditorController.version.props.id)}}">
{{customEditorController.version.props.name}}
{{customEditorController.version.props.name}}
</a>
</span>
<span ng-if="customEditorController.version && !customEditorController.isVersionFieldViewable">

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

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

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

@ -28,4 +28,5 @@
angular.module('openproject.workPackages.directives')
.directive('inplaceDisplayUser', require('./inplace-display-user-directive'))
.directive('inplaceDisplaySpentTime', require('./inplace-display-spent-time-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.
//++
module.exports = function(EditableFieldsState, PathHelper, VersionService) {
module.exports = function(EditableFieldsState, PathHelper, VersionService, $timeout) {
return {
restrict: 'E',
transclude: true,
@ -46,6 +46,13 @@ module.exports = function(EditableFieldsState, PathHelper, VersionService) {
scope.customEditorController.version = version;
VersionService.isVersionFieldViewable(EditableFieldsState.workPackage, displayPaneController.field).then(function(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() {
//element.on('click', 'a a', function(e) {
// console.log(e, 'adasd');
// e.stopPropagation();
// return false;
//});
element.find('a').on('click', function(e) {
console.log('a clicnk');
e.stopPropagation();
});
});

@ -39,7 +39,6 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, FocusHel
var fieldController = $scope.fieldController;
fieldController.isBusy = true;
var pendingFormChanges = getPendingFormChanges();
console.log(fieldController.writeValue, 'wv');
pendingFormChanges[fieldController.field] = fieldController.writeValue;
var result = WorkPackageService.updateWorkPackage(EditableFieldsState.workPackage, notify);
@ -60,10 +59,12 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState, FocusHel
this.discardEditing = function() {
$scope.fieldController.isEditing = false;
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();
};
this.getPendingFormChanges = getPendingFormChanges;
function getPendingFormChanges() {
var form = EditableFieldsState.workPackage.form;
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) {
if (isEditing) {
scope.editPaneController.error = null;

@ -29,8 +29,9 @@
module.exports = function(WorkPackageFieldService, EditableFieldsState) {
function workPackageFieldDirectiveController() {
this.isEditable = function() {
return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field)
return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field);
};
this.isEmpty = function() {
@ -46,19 +47,10 @@ module.exports = function(WorkPackageFieldService, EditableFieldsState) {
};
if (this.isEditable()) {
this.type = 'text';
this.isBusy = false;
this.isEditing = false;
this.updateWriteValue();
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) {
function isEditable(workPackage, field) {
// no form - no editing
if (!workPackage.form) {
return false;
}
// TODO: extract to strategy if new cases arise
if (field === 'date') {
// nope
@ -44,7 +48,7 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
var isWritable = workPackage.schema.props[field].writable;
// 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)) {
if (getEmbeddedAllowedValues(workPackage, field).length === 0) {
return false;
@ -53,6 +57,15 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
return isWritable;
}
function isSpecified(workPackage, field) {
if (field === 'date') {
// kind of specified
return true;
}
return !_.isUndefined(workPackage.schema
.props[field]);
}
function getValue(workPackage, field) {
if (!_.isUndefined(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) {
if (field === 'date') {
return getValue(workPackage, 'startDate') === null && getValue(workPackage, 'dueDate') === null;
}
var value = WorkPackageFieldService.getValue(workPackage, field);
return value === null || value === '';
}
@ -180,17 +196,23 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
displayStrategy = 'embedded';
if (field === 'date') {
fieldType = 'DateRange';
} else {
fieldType = workPackage.form.embedded.schema.props[field].type;
} else if (field === 'spentTime') {
fieldType = 'SpentTime';
} else {
fieldType = workPackage.schema.props[field].type;
}
switch(fieldType) {
case 'String':
case 'Integer':
case 'Float':
case 'Duration':
case 'DateRange':
case 'Date':
case 'Boolean':
displayStrategy = 'text';
break;
case 'Boolean':
displayStrategy = 'boolean';
case 'SpentTime':
displayStrategy = 'spent_time';
break;
case 'Formattable':
displayStrategy = 'wiki_textarea';
@ -207,10 +229,22 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
}
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];
if (_.isUndefined(value)) {
// might be embedded
return WorkPackageFieldService.getValue(workPackage, field);
}
if (value === null) {
return null;
}
var mappings = {
dueDate: 'date',
startDate: 'date',
@ -218,21 +252,23 @@ module.exports = function(I18n, WORK_PACKAGE_REGULAR_EDITABLE_FIELD, WorkPackage
updatedAt: 'datetime'
};
// TODO: switch to duration
if (field === 'estimatedTime' || field === 'spentTime') {
if (value === null) {
return null;
}
if (workPackage.schema.props[field].type === 'Duration') {
var hours = moment.duration(value).asHours();
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 = {
isEditable: isEditable,
isRequired: isRequired,
isSpecified: isSpecified,
isEmpty: isEmpty,
isEmbedded: isEmbedded,
isSavedAsLink: isSavedAsLink,

Loading…
Cancel
Save