Add calendar view

issue5756
review32361002
This commit is contained in:
Xavier Codinas
2016-10-26 09:15:05 +02:00
parent 970c92abfc
commit e814e196d8
6 changed files with 268 additions and 8 deletions
+1
View File
@@ -1,3 +1,4 @@
* Add calendar view
* Move info-bar to the top
* Add noeval to PYSONDecoder
* Add support for pyson_order on action window
+2 -1
View File
@@ -21,7 +21,8 @@
"eonasdan-bootstrap-datetimepicker": "~4.17.42",
"gettext.js": "~0.5.0",
"d3": "~3.5.0",
"c3": "~0.4.10"
"c3": "~0.4.10",
"fullcalendar": "~3.0.0"
},
"devDependencies": {
"qunit": "~1.18.0"
+3
View File
@@ -17,10 +17,13 @@ this repository contains the full copyright notices and license terms. -->
<script type="text/javascript" src="bower_components/gettext.js/dist/gettext.min.js"></script>
<script type="text/javascript" src="bower_components/d3/d3.min.js"></script>
<script type="text/javascript" src="bower_components/c3/c3.min.js"></script>
<script type="text/javascript" src="bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
<script type="text/javascript" src="bower_components/fullcalendar/dist/locale-all.js"></script>
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="bower_components/c3/c3.min.css">
<link rel="stylesheet" href="bower_components/fullcalendar/dist/fullcalendar.min.css">
<link rel="stylesheet" href="offcanvas.css">
<script type="text/javascript" src="dist/tryton-sao.min.js"></script>
+5
View File
@@ -216,6 +216,11 @@ button {
text-align: right;
}
.calendar {
width: 100%;
height: 100%;
}
.progress {
margin-bottom: 0px;
}
+17
View File
@@ -864,6 +864,15 @@
} else {
domain = this.attributes.domain || [];
}
if (this.current_view &&
this.current_view.view_type == 'calendar') {
if (!jQuery.isEmptyObject(domain)) {
domain = ['AND', domain,
this.current_view.current_domain()];
} else {
domain = this.current_view.current_domain();
}
}
return domain;
},
count_tab_domain: function() {
@@ -1036,10 +1045,15 @@
}
},
new_: function(default_, rec_name) {
var previous_view = this.current_view;
if (default_ === undefined) {
default_ = true;
}
var prm = jQuery.when();
if (this.current_view.view_type == 'calendar') {
var selected_date = this.current_view.date;
prm = this.switch_view('form');
}
if (this.current_view &&
((this.current_view.view_type == 'tree' &&
!this.current_view.editable) ||
@@ -1056,6 +1070,9 @@
var record = group.new_(default_, undefined, rec_name);
group.add(record, this.new_model_position());
this.set_current_record(record);
if (previous_view.view_type == 'calendar') {
previous_view.set_default_date(record, selected_date);
}
this.display().done(function() {
this.set_cursor(true, true);
}.bind(this));
+240 -7
View File
@@ -4,20 +4,253 @@
'use strict';
Sao.View.Calendar = Sao.class_(Sao.View, {
/* Fullcalendar works with utc date, the default week start day depends on
the user language, the events dates are handled by moment object. */
init: function(screen, xml) {
Sao.View.Graph._super.init.call(this, screen, xml);
Sao.View.Calendar._super.init.call(this, screen, xml);
// Used at display to reload the events when they are loaded.
this.prm = null;
this.view_type = 'calendar';
this.el = jQuery('<div/>', {
'class': 'calendar'
});
// TODO
Sao.common.warning.run(
Sao.i18n.gettext('Calendar view not yet implemented'),
Sao.i18n.gettext('Warning'));
var lang = Sao.i18n.getlang();
lang = lang.slice(0, 2);
var defaultview = 'month';
if (this.attributes.mode == 'week') {
defaultview = this.get_week_view();
}
this.el.fullCalendar({
defaultView: defaultview,
header: {
left: 'prev,next',
center: 'title',
right: ' month,' + this.get_week_view()
},
timeFormat: 'H:mm',
events: this.get_events.bind(this),
locale: lang,
buttonIcons: false,
eventRender: this.event_render.bind(this),
eventResize: this.event_resize.bind(this),
eventDrop: this.event_drop.bind(this),
eventClick: this.event_click.bind(this),
dayClick: this.day_click.bind(this),
});
this.fields = [];
xml.find('calendar').children().each(function(pos, child){
if (child.tagName == 'field') {
this.fields.push(child.attributes.name.value);
}
}.bind(this));
this.el.fullCalendar('changeView', defaultview);
},
display: function() {
return jQuery.when();
}
this.el.fullCalendar('render');
if (this.prm) {
this.prm.done(function() {
return this.el.fullCalendar('refetchEvents');
}.bind(this));
}
},
insert_event: function(record) {
var title = this.screen.model.fields[this.fields[0]].get_client(
record);
var date_start = record.field_get_client(this.attributes.dtstart);
var date_end = null;
if (this.attributes.dtend) {
date_end = record.field_get_client(this.attributes.dtend);
}
var allDay = true;
var description = [];
for (var i = 1; i < this.fields.length; i++) {
description.push(
this.screen.model.fields[this.fields[i]].get_client(
record));
}
description = description.join('\n');
if (date_start) {
if (date_end && date_end.isDateTime) {
allDay = false;
} else if (date_end && !date_end.isSame(date_start) &&
this.screen.current_view.view_type == "calendar") {
// Add one day to allday event that last more than one day.
// http://github.com/fullcalendar/fullcalendar/issues/2909
date_end.add(1, 'day');
}
// Skip invalid event
if (date_end && date_start > date_end) {
return;
}
var values = {
title: title,
start: date_start,
end: date_end,
allDay: allDay,
editable: true,
color: 'lightblue',
textColor: 'black',
record: record,
description: description
};
this.events.push(values);
}
},
get_events: function(start, end, timezone, callback) {
this.start = Sao.DateTime(start.utc());
this.end = Sao.DateTime(end.utc());
var prm = jQuery.when();
if (this.screen.current_view.view_type != 'form') {
var search_string = this.screen.screen_container.get_text();
prm = this.screen.search_filter(search_string);
}
this.events = [];
var promisses = [];
this.prm = prm.then(function() {
this.screen.group.forEach(function(record) {
var record_promisses = [];
this.fields.forEach(function(name) {
record_promisses.push(record.load(name));
});
var prm = jQuery.when.apply(jQuery, record_promisses).then(
function(){
this.insert_event(record);
}.bind(this));
promisses.push(prm);
}.bind(this));
jQuery.when.apply(jQuery, promisses).then(function() {
callback(this.events);
}.bind(this));
}.bind(this));
},
get_week_view: function() {
if (this.screen.model.fields[this.attributes.dtstart]
.description.type == "datetime") {
return 'agendaWeek';
} else {
return 'basicWeek';
}
},
event_click: function(calEvent, jsEvent, view) {
// Prevent opening the wrong event while the calendar event clicked
// when loading
if (!this.clicked_event) {
this.clicked_event = true;
this.screen.set_current_record(calEvent.record);
this.screen.switch_view().always(function(){
this.clicked_event = false;
}.bind(this));
}
},
event_drop: function(event, delta, revertFunc, jsEvent, ui, view) {
var dtstart = this.attributes.dtstart;
var dtend = this.attributes.dtend;
var record = event.record;
var group = record.group;
var previous_start = record.field_get(dtstart);
var previous_end = previous_start;
if (dtend) {
previous_end = record.field_get(dtend);
}
var new_start = event.start;
var new_end = event.end;
if (new_end == previous_start || !new_end) {
new_end = new_start;
}
if (previous_start.isDateTime) {
new_end = Sao.DateTime(new_end.format()).utc();
new_start = Sao.DateTime(new_start.format()).utc();
} else if (!previous_start.isSame(previous_end)) {
// Remove the day that was added at the event end.
new_end.subtract(1, 'day');
this.el.fullCalendar('refetchEvents');
}
if (previous_start <= new_start) {
if (dtend) {
record.field_set_client(dtend, new_end);
}
record.field_set_client(dtstart, new_start);
} else {
record.field_set_client(dtstart, new_start);
if (dtend) {
record.field_set_client(dtend, new_end);
}
}
record.save();
},
event_resize: function(event, delta, revertFunc, jsEvent, ui, view) {
var dtend = this.attributes.dtend;
var record = event.record;
var group = record.group;
var previous_end = record.field_get(dtend);
var new_end = event.end;
if (previous_end.isDateTime === true) {
new_end = Sao.DateTime(new_end.format()).utc();
} else {
// Remove the day that was added at the event end.
new_end.subtract(1, 'day');
this.el.fullCalendar('refetchEvents');
}
if (new_end == previous_end || !new_end) {
new_end = previous_end;
}
record.field_set_client(dtend, new_end);
record.save();
},
event_render: function(event, element, view) {
// The description field is added in the calendar events and the
// event time is not shown in week view.
if (this.screen.model.fields.date &&
this.screen.view_name == 'calendar') {
element.find('.fc-time').remove();
}
element.append(event.description);
element.css('white-space', 'pre');
var model_access = Sao.common.MODELACCESS.get(
this.screen.model_name);
if (!model_access.write) {
event.editable = false;
}
},
day_click: function(date, jsEvent, view){
var model_access = Sao.common.MODELACCESS.get(
this.screen.model_name);
if (model_access.create) {
this.date = date;
this.screen.set_current_record(null);
this.screen.new_();
}
},
current_domain: function() {
if (!this.start && !this.end) {
return [['id', '=', -1]];
}
var first_datetime = Sao.DateTime(this.start);
var last_datetime = Sao.DateTime(this.end);
var dtstart = this.attributes.dtstart;
var dtend = this.attributes.dtend || dtstart;
return ['OR',
['AND', [dtstart, '>=', first_datetime],
[dtstart, '<', last_datetime]],
['AND', [dtend, '>=', first_datetime],
[dtend, '<', last_datetime]],
['AND', [dtstart, '<', first_datetime],
[dtend, '>', last_datetime]]];
},
get_displayed_period: function(){
var DatesPeriod = [];
if (this.start && this.end) {
DatesPeriod.push(this.start, this.end);
}
return DatesPeriod;
},
set_default_date: function(record, selected_date){
var dtstart = this.attributes.dtstart;
if (record.model.fields[dtstart].description.type == 'datetime') {
selected_date = Sao.DateTime(selected_date.format()).utc();
}
record.field_set_client(dtstart, selected_date);
},
});
}());