Motion template fixes
- Add has_votes to motion serializer and add poll.has_votes check in motion detail view. - Fix motion meta box 3 column layout. - Added missing intents - Show motionPoll edit form in dialog. Moved into new template. - Clean up assignmentpoll form (like motionPoll)
This commit is contained in:
parent
440a38b387
commit
694ed6f1cc
@ -136,40 +136,6 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
}
|
||||
])
|
||||
|
||||
// Provide generic assignmentpoll form fields for create and update view
|
||||
.factory('AssignmentPollFormFieldFactory', [
|
||||
'gettextCatalog',
|
||||
function (gettextCatalog) {
|
||||
return {
|
||||
getFormFields: function () {
|
||||
return [
|
||||
{
|
||||
key: 'description',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Comment on the ballot paper')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'yes',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Yes'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'poll_description_default',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Default comment on the ballot paper')
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
.controller('AssignmentListCtrl', [
|
||||
'$scope',
|
||||
'ngDialog',
|
||||
@ -340,6 +306,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
template: 'static/templates/assignments/assignmentpoll-form.html',
|
||||
controller: 'AssignmentPollUpdateCtrl',
|
||||
className: 'ngdialog-theme-default',
|
||||
closeByEscape: false,
|
||||
closeByDocument: false,
|
||||
resolve: {
|
||||
assignmentpoll: function (AssignmentPoll) {
|
||||
return AssignmentPoll.find(poll.id);
|
||||
@ -443,6 +411,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
$scope.model = assignmentpoll;
|
||||
$scope.ballot = ballot;
|
||||
$scope.formFields = [];
|
||||
$scope.alert = {};
|
||||
|
||||
// add dynamic form fields
|
||||
assignmentpoll.options.forEach(function(option) {
|
||||
if (assignmentpoll.yesnoabstain) {
|
||||
@ -528,9 +498,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
// save assignment
|
||||
// save assignmentpoll
|
||||
$scope.save = function (poll) {
|
||||
var votes = [];
|
||||
if (assignmentpoll.yesnoabstain) {
|
||||
@ -556,8 +524,16 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||
votescast: poll.votescast
|
||||
})
|
||||
.then(function(success) {
|
||||
$scope.alert.show = false;
|
||||
$scope.closeThisDialog();
|
||||
})
|
||||
.catch(function(error) {
|
||||
var message = '';
|
||||
for (var e in error.data) {
|
||||
message += e + ': ' + error.data[e] + ' ';
|
||||
}
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
});
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
@ -1,8 +1,17 @@
|
||||
<h1><translate>Ballot</translate> {{ ballot }}</h1>
|
||||
|
||||
<form name="assignmentpollForm" ng-submit="save(model)">
|
||||
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||
{{ alert.msg }}
|
||||
</alert>
|
||||
|
||||
<p>
|
||||
<translate>Special values</translate>:
|
||||
<span class="badge badge-success">-1</span> = <translate>majority</translate>
|
||||
<span class="badge">-2</span> = <translate>undocumented</translate>
|
||||
|
||||
<form name="assignmentPollForm" ng-submit="save(model)">
|
||||
<formly-form model="model" fields="formFields">
|
||||
<button type="submit" ng-disabled="assignmentForm.$invalid" class="btn btn-primary" translate>
|
||||
<button type="submit" ng-disabled="assignmentPollForm.$invalid" class="btn btn-primary" translate>
|
||||
Save
|
||||
</button>
|
||||
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
|
||||
|
@ -100,6 +100,7 @@ class MotionPollSerializer(ModelSerializer):
|
||||
votes = DictField(
|
||||
child=IntegerField(min_value=-2, allow_null=True),
|
||||
write_only=True)
|
||||
has_votes = SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = MotionPoll
|
||||
@ -112,7 +113,8 @@ class MotionPollSerializer(ModelSerializer):
|
||||
'votesvalid',
|
||||
'votesinvalid',
|
||||
'votescast',
|
||||
'votes',)
|
||||
'votes',
|
||||
'has_votes')
|
||||
|
||||
def to_representation(self, obj):
|
||||
"""
|
||||
@ -150,6 +152,12 @@ class MotionPollSerializer(ModelSerializer):
|
||||
result = None
|
||||
return result
|
||||
|
||||
def get_has_votes(self, obj):
|
||||
"""
|
||||
Returns True if this poll has some votes.
|
||||
"""
|
||||
return obj.has_votes()
|
||||
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
|
@ -295,6 +295,69 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
}
|
||||
])
|
||||
|
||||
// Provide generic motionpoll form fields for poll update view
|
||||
.factory('MotionPollForm', [
|
||||
'gettextCatalog',
|
||||
function (gettextCatalog) {
|
||||
return {
|
||||
getFormFields: function () {
|
||||
return [
|
||||
{
|
||||
key: 'yes',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Yes'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'no',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('No'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'abstain',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Abstain'),
|
||||
type: 'number',
|
||||
required: true
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votesvalid',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Valid votes'),
|
||||
type: 'number'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votesinvalid',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Invalid votes'),
|
||||
type: 'number'
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'votescast',
|
||||
type: 'input',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Votes cast'),
|
||||
type: 'number'
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
.controller('MotionListCtrl', [
|
||||
'$scope',
|
||||
'$state',
|
||||
@ -430,58 +493,54 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
Workflow.bindAll({}, $scope, 'workflows');
|
||||
Motion.loadRelations(motion, 'agenda_item');
|
||||
|
||||
$scope.alert = {};
|
||||
$scope.isCollapsed = true;
|
||||
|
||||
// open edit dialog
|
||||
$scope.openDialog = function (motion) {
|
||||
ngDialog.open(MotionForm.getDialog(motion));
|
||||
};
|
||||
|
||||
// support
|
||||
$scope.support = function () {
|
||||
$http.post('/rest/motions/motion/' + motion.id + '/support/');
|
||||
}
|
||||
// unsupport
|
||||
$scope.unsupport = function () {
|
||||
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
||||
}
|
||||
// update state
|
||||
$scope.updateState = function (state_id) {
|
||||
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
||||
}
|
||||
// reset state
|
||||
$scope.reset_state = function (state_id) {
|
||||
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
|
||||
}
|
||||
// create poll
|
||||
$scope.create_poll = function () {
|
||||
$http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {})
|
||||
.success(function(data){
|
||||
$scope.alert.show = false;
|
||||
})
|
||||
.error(function(data){
|
||||
$scope.alert = { type: 'danger', msg: data.detail, show: true };
|
||||
});
|
||||
$http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {});
|
||||
}
|
||||
// open poll update dialog
|
||||
$scope.openPollDialog = function (poll, voteNumber) {
|
||||
ngDialog.open({
|
||||
template: 'static/templates/motions/motionpoll-form.html',
|
||||
controller: 'MotionPollUpdateCtrl',
|
||||
className: 'ngdialog-theme-default',
|
||||
closeByEscape: false,
|
||||
closeByDocument: false,
|
||||
resolve: {
|
||||
motionpoll: function (MotionPoll) {
|
||||
return MotionPoll.find(poll.id);
|
||||
},
|
||||
voteNumber: function () {
|
||||
return voteNumber;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
// delete poll
|
||||
$scope.delete_poll = function (poll) {
|
||||
poll.DSDestroy();
|
||||
}
|
||||
$scope.update_poll = function (poll) {
|
||||
poll.DSUpdate({
|
||||
motion_id: motion.id,
|
||||
votes: {"Yes": poll.yes, "No": poll.no, "Abstain": poll.abstain},
|
||||
votesvalid: poll.votesvalid,
|
||||
votesinvalid: poll.votesinvalid,
|
||||
votescast: poll.votescast
|
||||
})
|
||||
.then(function(success) {
|
||||
$scope.alert.show = false;
|
||||
poll.isEditMode = false;
|
||||
})
|
||||
.catch(function(error) {
|
||||
var message = '';
|
||||
for (var e in error.data) {
|
||||
message += e + ': ' + error.data[e] + ' ';
|
||||
}
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
});
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@ -587,6 +646,45 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
}
|
||||
])
|
||||
|
||||
.controller('MotionPollUpdateCtrl', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'gettextCatalog',
|
||||
'MotionPoll',
|
||||
'MotionPollForm',
|
||||
'motionpoll',
|
||||
'voteNumber',
|
||||
function($scope, $state, gettextCatalog, MotionPoll, MotionPollForm, motionpoll, voteNumber) {
|
||||
// set initial values for form model
|
||||
$scope.model = motionpoll;
|
||||
$scope.voteNumber = voteNumber;
|
||||
$scope.formFields = MotionPollForm.getFormFields();
|
||||
$scope.alert = {};
|
||||
|
||||
// save motionpoll
|
||||
$scope.save = function (poll) {
|
||||
poll.DSUpdate({
|
||||
motion_id: poll.motion_id,
|
||||
votes: {"Yes": poll.yes, "No": poll.no, "Abstain": poll.abstain},
|
||||
votesvalid: poll.votesvalid,
|
||||
votesinvalid: poll.votesinvalid,
|
||||
votescast: poll.votescast
|
||||
})
|
||||
.then(function(success) {
|
||||
$scope.alert.show = false;
|
||||
$scope.closeThisDialog();
|
||||
})
|
||||
.catch(function(error) {
|
||||
var message = '';
|
||||
for (var e in error.data) {
|
||||
message += e + ': ' + error.data[e] + ' ';
|
||||
}
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
});
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.controller('MotionImportCtrl', [
|
||||
'$scope',
|
||||
'gettext',
|
||||
@ -759,6 +857,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
.controller('CategoryListCtrl', function($scope, Category) {
|
||||
Category.bindAll({}, $scope, 'categories');
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
</div>
|
||||
<div class="detail" uib-collapse="isMeta">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="col-md-4">
|
||||
<!-- submitters -->
|
||||
<h3 translate>Submitters</h3>
|
||||
<div ng-repeat="submitter in motion.submitters">
|
||||
@ -78,7 +78,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="col-md-4">
|
||||
<!-- Category -->
|
||||
<h3 ng-if="motion.category" translate>Category</h3>
|
||||
{{ motion.category.name }}
|
||||
@ -106,71 +106,26 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="col-md-4">
|
||||
<h3 ng-if="motion.polls.length > 0" translate>Voting result</h3>
|
||||
<ol class="slimlist">
|
||||
<li ng-repeat="poll in motion.polls" class="spacer">
|
||||
<translate translate-context="ballot">Vote</translate>
|
||||
<button os-perms="motions.can_manage" ng-click="$parent.poll.isEditMode=true;"
|
||||
class="btn btn-default btn-xs">
|
||||
<li ng-repeat="poll in motion.polls" class="spacer"
|
||||
ng-if="poll.has_votes || operator.hasPerms('motions.can_manage')">
|
||||
<strong><translate translate-context="ballot">Vote</translate></strong>
|
||||
<!-- Edit poll -->
|
||||
<button os-perms="motions.can_manage" ng-click="openPollDialog(poll, $index+1)"
|
||||
class="btn btn-default btn-xs" title="{{ 'Edit' | translate }}">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button os-perms="motions.can_manage" ng-click="delete_poll(poll)"
|
||||
class="btn btn-default btn-xs">
|
||||
<!-- Delete poll -->
|
||||
<button os-perms="motions.can_manage" class="btn btn-default btn-xs"
|
||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this poll?' | translate }}"
|
||||
ng-bootbox-confirm-action="delete_poll(poll)"
|
||||
title="{{ 'Delete' | translate }}">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
<div ng-show="poll.isEditMode" class="spacer">
|
||||
<form>
|
||||
<p>
|
||||
<translate>Special values</translate>:<br>
|
||||
<span class="badge badge-success">-1</span> = <translate>majority</translate><br>
|
||||
<span class="badge">-2</span> = <translate>undocumented</translate>
|
||||
</p>
|
||||
<alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||
{{alert.msg}}
|
||||
</alert>
|
||||
<!-- yes -->
|
||||
<div class="input-group col-sm-8 spacer">
|
||||
<div class="input-group-addon" title="{{ 'Yes' | translate }}"><i class="fa fa-thumbs-up"></i></div>
|
||||
<input type="number" ng-model="poll.yes" class="form-control input-sm" placeholder="{{ 'Yes' | translate }}">
|
||||
</div>
|
||||
<!-- no -->
|
||||
<div class="input-group col-sm-8">
|
||||
<div class="input-group-addon" title="{{ 'No' | translate }}"><i class="fa fa-thumbs-down"></i></div>
|
||||
<input type="number" ng-model="poll.no" class="form-control input-sm" placeholder="{{ 'No' | translate }}">
|
||||
</div>
|
||||
<!-- abstain -->
|
||||
<div class="input-group col-sm-8">
|
||||
<div class="input-group-addon" title="{{ 'Abstain' | translate }}"><b>∅</b></div>
|
||||
<input type="number" ng-model="poll.abstain" class="form-control input-sm" placeholder="{{ 'Abstain' | translate }}">
|
||||
</div>
|
||||
<!-- valid votes -->
|
||||
<div class="input-group col-sm-8 spacer">
|
||||
<div class="input-group-addon" title="{{ 'Valid votes' | translate }}"><i class="fa fa-check"></i></div>
|
||||
<input type="number" ng-model="poll.votesvalid" class="form-control input-sm" placeholder="{{ 'Valid votes' | translate }}">
|
||||
</div>
|
||||
<!-- invalid votes -->
|
||||
<div class="input-group col-sm-8">
|
||||
<div class="input-group-addon" title="{{ 'Invalid votes' | translate }}"><i class="fa fa-ban"></i></div>
|
||||
<input type="number" ng-model="poll.votesinvalid" class="form-control input-sm" placeholder="{{ 'Invalid votes' | translate }}">
|
||||
</div>
|
||||
<!-- votes cast -->
|
||||
<div class="input-group col-sm-8 spacer">
|
||||
<div class="input-group-addon" title="{{ 'Votes cast' | translate }}"><b>∑</b></div>
|
||||
<input type="number" ng-model="poll.votescast" class="form-control input-sm" placeholder="{{ 'Votes cast' | translate }}">
|
||||
</div>
|
||||
<!-- buttons -->
|
||||
<div class="spacer">
|
||||
<button type="submit" ng-click="update_poll(poll)" class="btn btn-primary" translate>
|
||||
Save
|
||||
</button>
|
||||
<button ng-click="poll.isEditMode=false;" class="btn btn-default" translate>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div ng-show="!poll.isEditMode" class="pollresults">
|
||||
<!-- Poll results -->
|
||||
<div ng-show="poll.has_votes" class="pollresults">
|
||||
<table class="table">
|
||||
<!-- yes -->
|
||||
<tr>
|
||||
|
Loading…
Reference in New Issue
Block a user