Merge pull request #18 from alberanid/master

easy way to create a new person from the event detail page
This commit is contained in:
Davide Alberani 2015-04-08 00:00:55 +02:00
commit 029d9e8c8f
5 changed files with 156 additions and 46 deletions

View file

@ -10,7 +10,7 @@ Definitions:
Requirements:
- create a new event (**DONE**)
- create a new registered person manually (**DONE**)
- associate to an event a list of registered persons, creating them if needed (manually and importing from external sources)
- associate to an event a list of registered persons, creating them if needed (manually and importing from external sources) (**DONE**)
- mark registered persons as present (including them in the list of attendees) (**DONE**)
- mark when an attendee enters/leaves the event
- execute actions when an attendee shows up or enters/leaves the event
@ -99,7 +99,6 @@ TODO
Next to be done
---------------
- easy way to add a new person to an event
- add the minimum required fields to lists and detailed pages for persons and events
- handle datetimes (on GUI with a calendar and on the backend deserializing ISO 8601 strings)
- modal on event/person removal

View file

@ -4,12 +4,8 @@ Event Man(ager)
Your friendly manager of attendees at an event.
Notice
======
No, this project is not ready, yet.
I'll let you know when I'm finished experimenting with it and you can contribute.
Development
===========
See the DEVELOPMENT.md file for more information about how to contribute.
@ -38,6 +34,8 @@ Python code documented following the [Sphinx](http://sphinx-doc.org/) syntax.
Install and run
===============
Be sure to have a running MongoDB server, locally.
wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install tornado

View file

@ -6,44 +6,106 @@
{{'Edit' | translate}}
</button>
</h1>
<div class="panel panel-primary table-striped top5">
<div class="panel-heading">{{'Persons' | translate}}</div>
<div class="panel-body">
<form class="form-inline">
<div class="form-group">
<label for="query-persons">{{'Search:' | translate}}</label>
<input type="text" id="query-persons" class="form-control" placeholder="{{'Name or email' | translate}}" ng-model="query">
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="panel panel-primary table-striped top5">
<div class="panel-heading">{{'Persons' | translate}}</div>
<div class="panel-body">
<form class="form-inline">
<div class="form-group">
<label for="query-persons">{{'Search:' | translate}}</label>
<input type="text" id="query-persons" class="form-control" placeholder="{{'Name or email' | translate}}" ng-model="query">
</div>
<div class="form-group">
<label for="persons-order">{{'Sort by:' | translate}}</label>
<select id="persons-order" class="form-control" ng-model="personsOrderProp">
<option value="name" ng-selected="selected">{{'Name' | translate}}</option>
<option value="-name" ng-selected="selected">{{'Name (descending)' | translate}}</option>
<option value="surname" ng-selected="selected">{{'Surname' | translate}}</option>
<option value="-surname" ng-selected="selected">{{'Surname (descending)' | translate}}</option>
</select>
</div>
</form>
<table class="table table-striped">
<thead>
<tr>
<th>{{'Person' | translate}}</th>
<th>{{'Email' | translate}}</th>
<th>{{'Attended' | translate}}</th>
<th>{{'Actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in event.persons | splittedFilter:query | orderBy:personsOrderProp">
<td><strong><a ui-sref="person.info({id: person.person_id})">{{person.name}} {{person.surname}}</a></strong></td>
<td>{{person.email}}</td>
<td>
<button class="btn btn-link" name="switch-attended" ng-click="updateAttendee(person, !person.attended)"><span class="glyphicon {{(person.attended) && 'glyphicon-ok-sign text-success' || 'glyphicon-remove-sign text-danger'}}"></span></button>
</td>
<td>
<button ng-click="removeAttendee(person)" type="button" class="btn btn-link glyphicon glyphicon-trash"></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group">
<label for="persons-order">{{'Sort by:' | translate}}</label>
<select id="persons-order" class="form-control" ng-model="personsOrderProp">
<option value="name" ng-selected="selected">{{'Name' | translate}}</option>
<option value="-name" ng-selected="selected">{{'Name (descending)' | translate}}</option>
<option value="surname" ng-selected="selected">{{'Surname' | translate}}</option>
<option value="-surname" ng-selected="selected">{{'Surname (descending)' | translate}}</option>
</select>
</div>
<div class="col-md-4">
<div class="panel panel-info table-striped top5">
<div class="panel-heading">{{'Fast add' | translate}}</div>
<div class="panel-body">
<form>
<div class="input-group input-group-sm">
<span class="input-group-addon">{{'Name' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Name' | translate}}" ng-model="newPerson.name" ng-required="1">
</div>
<div class="input-group input-group-sm top5">
<span class="input-group-addon">{{'Surname' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Surname' | translate}}" ng-model="newPerson.surname">
</div>
<div class="input-group top5">
<span class="input-group-addon">{{'Email' | translate}}</span>
<input type="email" name="email" class="form-control" placeholder="{{'name.surname@example.com' | translate}}" ng-model="newPerson.email">
</div>
<button ng-disabled="!(newPerson.name && newPerson.surname)" ng-click="fastAddPerson(newPerson, true)" class="btn btn-success top5">
<span class="glyphicon glyphicon-plus-sign"></span>
{{'Add' | translate}}
</button>
</form>
</div>
</div>
</form>
<table class="table table-striped">
<thead>
<tr>
<th>{{'Person' | translate}}</th>
<th>{{'Attended' | translate}}</th>
<th>{{'Actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in event.persons | splittedFilter:query | orderBy:personsOrderProp">
<td><strong><a ui-sref="person.info({id: person.person_id})">{{person.name}} {{person.surname}}</a></strong></td>
<td>
<button class="btn btn-link" name="switch-attended" ng-click="updateAttendee(person, !person.attended)"><span class="glyphicon {{(person.attended) && 'glyphicon-ok-sign text-success' || 'glyphicon-remove-sign text-danger'}}"></span></button>
</td>
<td>
<button ng-click="removeAttendee(person)" type="button" class="btn btn-link glyphicon glyphicon-trash"></button>
</td>
</tr>
</tbody>
</table>
<div class="panel panel-info table-striped top5">
<div class="panel-heading">{{'Unregistered persons' | translate}}</div>
<div class="panel-body" style="height:200px;overflow:auto;">
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{{'Person' | translate}}</th>
<th>{{'Add' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in allPersons | filter:newPerson | personRegistered:{event: event, present: false}">
<td>
<strong><a ui-sref="person.info({id: person._id})">{{person.name}} {{person.surname}}</a></strong>
<br />
{{person.email}}
</td>
<td>
<button ng-click="fastAddPerson(person)" type="button" class="btn btn-link glyphicon glyphicon-plus-sign"></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>

25
angular_app/js/app.js vendored
View file

@ -69,6 +69,31 @@ eventManApp.filter('eventWithPersonData', ['$filter',
}]
);
eventManApp.filter('personRegistered', ['$filter',
function($filter) {
return function(inputArray, data) {
if (data.present === undefined) {
data.present = true;
}
inputArray = inputArray || [];
var returnArray = [];
var registeredIDs = [];
if (!(data.event && data.event.persons && data.event.persons.length)) {
return inputArray;
}
for (var x=0; x < data.event.persons.length; x++) {
registeredIDs.push(data.event.persons[x].person_id);
}
for (var x=0; x < inputArray.length; x++) {
var found = registeredIDs.indexOf(inputArray[x]._id) != -1;
if ((found && data.present) || (!found && !data.present)) {
returnArray.push(inputArray[x]);
}
}
return returnArray;
}
}]
);
/* Directive that can be used to make an input field react to the press of Enter. */
eventManApp.directive('ngEnter', function () {

View file

@ -53,12 +53,13 @@ eventManControllers.controller('EventsListCtrl', ['$scope', 'Event',
);
eventManControllers.controller('EventDetailsCtrl', ['$scope', 'Event', '$stateParams', '$log',
function ($scope, Event, $stateParams, $log) {
eventManControllers.controller('EventDetailsCtrl', ['$scope', 'Event', 'Person', '$stateParams', '$log',
function ($scope, Event, Person, $stateParams, $log) {
$scope.personsOrderProp = 'name';
$scope.eventsOrderProp = '-begin-date';
if ($stateParams.id) {
$scope.event = Event.get($stateParams);
$scope.allPersons = Person.all();
}
// store a new Event or update an existing one
$scope.save = function() {
@ -75,6 +76,30 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', 'Event', '$statePa
$scope.eventForm.$dirty = false;
};
$scope._addAttendee = function(person_data) {
person_data.person_id = person_data._id;
person_data._id = $stateParams.id;
person_data.attended = true;
Event.addAttendee(person_data, function() {
$scope.event = Event.get($stateParams);
$scope.allPersons = Person.all();
$scope.newPerson = {};
});
};
$scope.fastAddPerson = function(person, isNew) {
$log.debug('EventDetailsCtrl.fastAddPerson.person:');
$log.debug(person);
if (isNew) {
var personObj = new Person(person);
personObj.$save(function(p) {
$scope._addAttendee(angular.copy(p));
});
} else {
$scope._addAttendee(angular.copy(person));
}
};
$scope.updateAttendee = function(person, attended) {
$log.debug('EventDetailsCtrl.event_id: ' + $stateParams.id);
$log.debug('EventDetailsCtrl.person_id: ' + person.person_id);
@ -98,6 +123,7 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', 'Event', '$statePa
},
function(data) {
$scope.event.persons = data;
$scope.allPersons = Person.all();
});
};
}]