mirror of https://github.com/OCA/web.git
[MIG] web_timeline: Migration to 12.0
parent
82af9cc159
commit
8d6b546bab
|
@ -1,16 +1,40 @@
|
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||
============
|
||||
Web timeline
|
||||
============
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Production/Stable
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/web/tree/12.0/web_timeline
|
||||
:alt: OCA/web
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_timeline
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/162/12.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
=============
|
||||
Timeline view
|
||||
=============
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Define a new view displaying events in an interactive visualization chart.
|
||||
|
||||
The widget is based on the external library
|
||||
http://visjs.org/timeline_examples.html
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
|
@ -119,11 +143,6 @@ new record with the group and start date linked to the area you clicked in.
|
|||
By holding the Ctrl key and dragging left to right, you can create a new record
|
||||
with the dragged start and end date.
|
||||
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/162/11.0
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
|
@ -132,21 +151,26 @@ Known issues / Roadmap
|
|||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues
|
||||
<https://github.com/OCA/web/issues>`_. In case of trouble, please
|
||||
check there if your issue has already been reported. If you spotted it first,
|
||||
help us smashing it by providing a detailed and welcomed feedback.
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us smashing it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_timeline%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
Authors
|
||||
~~~~~~~
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
* ACSONE SA/NV
|
||||
* Tecnativa
|
||||
* Monk Software
|
||||
* Onestein
|
||||
|
||||
Contributors
|
||||
------------
|
||||
~~~~~~~~~~~~
|
||||
|
||||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
||||
|
@ -155,19 +179,35 @@ Contributors
|
|||
* Adrien Didenot <adrien.didenot@horanet.com>
|
||||
* Dennis Sluijk <d.sluijk@onestein.nl>
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
Other credits
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
|
||||
Maintainers
|
||||
~~~~~~~~~~~
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
||||
.. |maintainer-tarteo| image:: https://github.com/tarteo.png?size=40px
|
||||
:target: https://github.com/tarteo
|
||||
:alt: tarteo
|
||||
|
||||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`_:
|
||||
|
||||
|maintainer-tarteo|
|
||||
|
||||
This module is part of the `OCA/web <https://github.com/OCA/web/tree/12.0/web_timeline>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
{
|
||||
'name': "Web timeline",
|
||||
'summary': "Interactive visualization chart to show events in time",
|
||||
"version": "11.0.1.4.0",
|
||||
"version": "12.0.1.0.0",
|
||||
"development_status": "Production/Stable",
|
||||
'author': 'ACSONE SA/NV, '
|
||||
'Tecnativa, '
|
||||
'Monk Software, '
|
||||
|
@ -14,7 +15,7 @@
|
|||
"license": "AGPL-3",
|
||||
"application": False,
|
||||
"installable": True,
|
||||
"website": "http://acsone.eu",
|
||||
"website": "https://github.com/OCA/web",
|
||||
'depends': [
|
||||
'web',
|
||||
],
|
||||
|
@ -24,4 +25,5 @@
|
|||
'data': [
|
||||
'views/web_timeline.xml',
|
||||
],
|
||||
"maintainers": ["tarteo"],
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
You need to define a view with the tag <timeline> as base element. These are
|
||||
the possible attributes for the tag:
|
||||
|
||||
* date_start (required): it defines the name of the field of type date that
|
||||
contains the start of the event.
|
||||
* date_stop (optional): it defines the name of the field of type date that
|
||||
contains the end of the event. The date_stop can be equal to the attribute
|
||||
date_start to display events has 'point' on the Timeline (instantaneous event)
|
||||
* date_delay (optional): it defines the name of the field of type float/integer
|
||||
that contain the duration in hours of the event, default = 1
|
||||
* default_group_by (required): it defines the name of the field that will be
|
||||
taken as default group by when accessing the view or when no other group by
|
||||
is selected.
|
||||
* zoomKey (optional): Specifies whether the Timeline is only zoomed when an
|
||||
additional key is down. Available values are '' (does not apply), 'altKey',
|
||||
'ctrlKey', or 'metaKey'. Set this option if you want to be able to use the
|
||||
scroll to navigate vertically on views with a lot of events.
|
||||
* mode (optional): Specifies the initial visible window. Available values are:
|
||||
'day' to display the current day, 'week', 'month' and 'fit'.
|
||||
Default value is 'fit' to adjust the visible window such that it fits all items
|
||||
* event_open_popup (optional): when set to true, it allows to edit the events
|
||||
in a popup. If not (default value), the record is edited changing to form
|
||||
view.
|
||||
* colors (optional): it allows to set certain specific colors if the expressed
|
||||
condition (JS syntax) is met.
|
||||
* dependency_arrow (optional): set this attribute to a x2many field to draw
|
||||
arrows between the records referenced in the x2many field.
|
||||
|
||||
Optionally you can declare a custom template, which will be used to render the
|
||||
timeline items. You have to name the template 'timeline-item'.
|
||||
These are the variables available in template rendering:
|
||||
|
||||
* ``record``: to access the fields values selected in the timeline definition.
|
||||
* ``field_utils``: used to format and parse values (see available functions in ``web.field_utils``).
|
||||
|
||||
You also need to declare the view in an action window of the involved model.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="view_task_timeline" model="ir.ui.view">
|
||||
<field name="model">project.task</field>
|
||||
<field name="type">timeline</field>
|
||||
<field name="arch" type="xml">
|
||||
<timeline date_start="date_start"
|
||||
date_stop="date_end"
|
||||
string="Tasks"
|
||||
default_group_by="user_id"
|
||||
event_open_popup="true"
|
||||
zoomKey="ctrlKey"
|
||||
colors="#ec7063:user_id == false;#2ecb71:kanban_state=='done';"
|
||||
dependency_arrow="task_dependency_ids">
|
||||
<field name="user_id"/>
|
||||
<templates>
|
||||
<div t-name="timeline-item">
|
||||
<div t-esc="record.display_name"/>
|
||||
Assigned to:
|
||||
<span t-esc="record.user_id[1]"/>
|
||||
</div>
|
||||
</templates>
|
||||
</timeline>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project.action_view_task" model="ir.actions.act_window">
|
||||
<field name="view_mode">kanban,tree,form,calendar,gantt,timeline,graph</field>
|
||||
</record>
|
||||
</odoo>
|
|
@ -0,0 +1,6 @@
|
|||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||
* Adrien Peiffer <adrien.peiffer@acsone.eu>
|
||||
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
|
||||
* Leonardo Donelli <donelli@webmonks.it>
|
||||
* Adrien Didenot <adrien.didenot@horanet.com>
|
||||
* Dennis Sluijk <d.sluijk@onestein.nl>
|
|
@ -0,0 +1,4 @@
|
|||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
|
@ -0,0 +1,4 @@
|
|||
Define a new view displaying events in an interactive visualization chart.
|
||||
|
||||
The widget is based on the external library
|
||||
http://visjs.org/timeline_examples.html
|
|
@ -0,0 +1 @@
|
|||
* Implement a more efficient way of refreshing timeline after a record update.
|
|
@ -0,0 +1,29 @@
|
|||
For accessing the timeline view, you have to click on the button with the clock
|
||||
icon in the view switcher. The first time you access to it, the timeline window
|
||||
is zoomed to fit all the current elements, the same as when you perform a
|
||||
search, filter or group by operation.
|
||||
|
||||
You can use the mouse scroll to zoom in or out in the timeline, and click on
|
||||
any free area and drag for panning the view in that direction.
|
||||
|
||||
The records of your model will be shown as rectangles whose widths are the
|
||||
duration of the event according our definition. You can select them clicking
|
||||
on this rectangle. You can also use Ctrl or Shift keys for adding discrete
|
||||
or range selections. Selected records are hightlighted with a different color
|
||||
(but the difference will be more noticeable depending on the background color).
|
||||
Once selected, you can drag and move the selected records across the timeline.
|
||||
|
||||
When a record is selected, a red cross button appears on the upper left corner
|
||||
that allows to remove that record. This doesn't work for multiple records
|
||||
although they were selected.
|
||||
|
||||
Records are grouped in different blocks depending on the group by criteria
|
||||
selected (if none is specified, then the default group by is applied).
|
||||
Dragging a record from one block to another change the corresponding field to
|
||||
the value that represents the block. You can also click on the group name to
|
||||
edit the involved record directly.
|
||||
|
||||
Double-click on the record to edit it. Double-click in open area to create a
|
||||
new record with the group and start date linked to the area you clicked in.
|
||||
By holding the Ctrl key and dragging left to right, you can create a new record
|
||||
with the dragged start and end date.
|
|
@ -5,14 +5,37 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
"use strict";
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
/**
|
||||
* Used to draw stuff on upon the timeline view.
|
||||
*/
|
||||
var TimelineCanvas = Widget.extend({
|
||||
template: 'TimelineView.Canvas',
|
||||
|
||||
clear: function() {
|
||||
/**
|
||||
* Clears all drawings (svg elements) from the canvas.
|
||||
*/
|
||||
clear: function () {
|
||||
this.$el.find(' > :not(defs)').remove();
|
||||
},
|
||||
|
||||
get_polyline_points: function(coordx1, coordy1, coordx2, coordy2, width1, height1, width2, height2, widthMarker, breakAt) {
|
||||
/**
|
||||
* Gets the path from one point to another.
|
||||
*
|
||||
* @param {Number} coordx1
|
||||
* @param {Number} coordy1
|
||||
* @param {Number} coordx2
|
||||
* @param {Number} coordy2
|
||||
* @param {Number} width1
|
||||
* @param {Number} height1
|
||||
* @param {Number} width2
|
||||
* @param {Number} height2
|
||||
* @param {Number} widthMarker The marker's width of the polyline
|
||||
* @param {Number} breakAt The space between the line turns
|
||||
* @returns {Array} Each item represents a coordinate
|
||||
*/
|
||||
get_polyline_points: function (coordx1, coordy1, coordx2, coordy2,
|
||||
width1, height1, width2, height2,
|
||||
widthMarker, breakAt) {
|
||||
var halfHeight1 = height1 / 2;
|
||||
var halfHeight2 = height2 / 2;
|
||||
var x1 = coordx1 - widthMarker;
|
||||
|
@ -47,11 +70,32 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
return points;
|
||||
},
|
||||
|
||||
draw_arrow: function(from, to, color, width) {
|
||||
/**
|
||||
* Draws an arrow.
|
||||
*
|
||||
* @param {HTMLElement} from Element to draw the arrow from
|
||||
* @param {HTMLElement} to Element to draw the arrow to
|
||||
* @param {String} color Color of the line
|
||||
* @param {Number} width Width of the line
|
||||
* @returns {HTMLElement} The created SVG polyline
|
||||
*/
|
||||
draw_arrow: function (from, to, color, width) {
|
||||
return this.draw_line(from, to, color, width, '#arrowhead', 10, 12);
|
||||
},
|
||||
|
||||
draw_line: function(from, to, color, width, markerStart, widthMarker, breakLineAt) {
|
||||
/**
|
||||
* Draws a line.
|
||||
*
|
||||
* @param {HTMLElement} from Element to draw the line from
|
||||
* @param {HTMLElement} to Element to draw the line to
|
||||
* @param {String} color Color of the line
|
||||
* @param {Number} width Width of the line
|
||||
* @param {String} markerStart Start marker of the line
|
||||
* @param {Number} widthMarker The marker's width of the polyline
|
||||
* @param {Number} breakLineAt The space between the line turns
|
||||
* @returns {HTMLElement} The created SVG polyline
|
||||
*/
|
||||
draw_line: function (from, to, color, width, markerStart, widthMarker, breakLineAt) {
|
||||
var x1 = from.offsetLeft,
|
||||
y1 = from.offsetTop + from.parentElement.offsetTop,
|
||||
x2 = to.offsetLeft,
|
||||
|
@ -81,8 +125,7 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
}
|
||||
this.$el.append(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
return TimelineCanvas;
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
odoo.define('web_timeline.TimelineController', function (require) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
var AbstractController = require('web.AbstractController');
|
||||
var dialogs = require('web.view_dialogs');
|
||||
var core = require('web.core');
|
||||
var time = require('web.time');
|
||||
var Dialog = require('web.Dialog');
|
||||
var AbstractController = require('web.AbstractController');
|
||||
var dialogs = require('web.view_dialogs');
|
||||
var core = require('web.core');
|
||||
var time = require('web.time');
|
||||
var Dialog = require('web.Dialog');
|
||||
|
||||
var _t = core._t;
|
||||
var _t = core._t;
|
||||
|
||||
var CalendarController = AbstractController.extend({
|
||||
var TimelineController = AbstractController.extend({
|
||||
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
|
||||
onGroupClick: '_onGroupClick',
|
||||
onUpdate: '_onUpdate',
|
||||
|
@ -18,6 +18,10 @@ var CalendarController = AbstractController.extend({
|
|||
onAdd: '_onAdd',
|
||||
}),
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, model, renderer, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.open_popup_action = params.open_popup_action;
|
||||
|
@ -29,7 +33,10 @@ var CalendarController = AbstractController.extend({
|
|||
this.debouncedInternalMove = _.debounce(this.internalMove, 0);
|
||||
},
|
||||
|
||||
update: function(params, options) {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
update: function (params, options) {
|
||||
this._super.apply(this, arguments);
|
||||
if (_.isEmpty(params)){
|
||||
return;
|
||||
|
@ -69,6 +76,12 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets triggered when a group in the timeline is clicked (by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_onGroupClick: function (event) {
|
||||
var groupField = this.renderer.last_group_bys[0];
|
||||
return this.do_action({
|
||||
|
@ -80,7 +93,12 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
_onUpdate: function(event) {
|
||||
/**
|
||||
* Opens a form view of a clicked timeline item (triggered by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onUpdate: function (event) {
|
||||
var self = this;
|
||||
this.renderer = event.data.renderer;
|
||||
var rights = event.data.rights;
|
||||
|
@ -112,7 +130,12 @@ var CalendarController = AbstractController.extend({
|
|||
}
|
||||
},
|
||||
|
||||
_onMove: function(event) {
|
||||
/**
|
||||
* Gets triggered when a timeline item is moved (triggered by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onMove: function (event) {
|
||||
var item = event.data.item;
|
||||
var view = this.renderer.view;
|
||||
var fields = view.fields;
|
||||
|
@ -150,7 +173,13 @@ var CalendarController = AbstractController.extend({
|
|||
this.debouncedInternalMove();
|
||||
},
|
||||
|
||||
internalMove: function() {
|
||||
/**
|
||||
* Write enqueued moves to Odoo. After all writes are finished it updates the view once
|
||||
* (prevents flickering of the view when multiple timeline items are moved at once).
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
internalMove: function () {
|
||||
var self = this;
|
||||
var queue = this.moveQueue.slice();
|
||||
this.moveQueue = [];
|
||||
|
@ -175,7 +204,14 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
_onRemove: function(e) {
|
||||
/**
|
||||
* Triggered when a timeline item gets removed from the view.
|
||||
* Requires user confirmation before it gets actually deleted.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_onRemove: function (e) {
|
||||
var self = this;
|
||||
|
||||
function do_it(event) {
|
||||
|
@ -186,9 +222,9 @@ var CalendarController = AbstractController.extend({
|
|||
[event.data.item.id],
|
||||
],
|
||||
context: self.getSession().user_context,
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
var unlink_index = false;
|
||||
for (var i=0; i<self.model.data.data.length; i++) {
|
||||
for (var i = 0; i < self.model.data.data.length; i++) {
|
||||
if (self.model.data.data[i].id === event.data.item.id) {
|
||||
unlink_index = i;
|
||||
}
|
||||
|
@ -214,7 +250,12 @@ var CalendarController = AbstractController.extend({
|
|||
return def.promise();
|
||||
},
|
||||
|
||||
_onAdd: function(event) {
|
||||
/**
|
||||
* Triggered when a timeline item gets added and opens a form view.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onAdd: function (event) {
|
||||
var self = this;
|
||||
var item = event.data.item;
|
||||
// Initialize default values for creation
|
||||
|
@ -224,10 +265,14 @@ var CalendarController = AbstractController.extend({
|
|||
default_context['default_'.concat(this.date_delay)] = 1;
|
||||
}
|
||||
if (this.date_start) {
|
||||
default_context['default_'.concat(this.date_start)] = moment(item.start).add(1, 'hours').toDate();
|
||||
default_context['default_'.concat(this.date_start)] = moment(item.start).add(1, 'hours').format(
|
||||
'YYYY-MM-DD HH:mm:ss'
|
||||
);
|
||||
}
|
||||
if (this.date_stop && item.end) {
|
||||
default_context['default_'.concat(this.date_stop)] = moment(item.end).add(1, 'hours').toDate();
|
||||
default_context['default_'.concat(this.date_stop)] = moment(item.end).add(1, 'hours').format(
|
||||
'YYYY-MM-DD HH:mm:ss'
|
||||
);
|
||||
}
|
||||
if (item.group > 0) {
|
||||
default_context['default_'.concat(this.renderer.last_group_bys[0])] = item.group;
|
||||
|
@ -241,13 +286,19 @@ var CalendarController = AbstractController.extend({
|
|||
on_saved: function (record) {
|
||||
self.create_completed([record.res_id]);
|
||||
},
|
||||
}).open().on('closed', this, function() {
|
||||
}).open().on('closed', this, function () {
|
||||
event.data.callback();
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered upon completion of a new record.
|
||||
* Updates the timeline view with the new record.
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
create_completed: function (id) {
|
||||
var self = this;
|
||||
return this._rpc({
|
||||
|
@ -268,6 +319,9 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered upon completion of writing a record.
|
||||
*/
|
||||
write_completed: function (options) {
|
||||
var params = {
|
||||
domain: this.renderer.last_domains,
|
||||
|
@ -277,7 +331,7 @@ var CalendarController = AbstractController.extend({
|
|||
|
||||
this.update(params, options);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return CalendarController;
|
||||
return TimelineController;
|
||||
});
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
odoo.define('web_timeline.TimelineModel', function (require) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
var AbstractModel = require('web.AbstractModel');
|
||||
var AbstractModel = require('web.AbstractModel');
|
||||
|
||||
var TimelineModel = AbstractModel.extend({
|
||||
var TimelineModel = AbstractModel.extend({
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
load: function (params) {
|
||||
var self = this;
|
||||
this.modelName = params.modelName;
|
||||
|
@ -34,6 +41,12 @@ var TimelineModel = AbstractModel.extend({
|
|||
return this.preload_def.then(this._loadTimeline.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Read the records for the timeline.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_loadTimeline: function () {
|
||||
var self = this;
|
||||
return self._rpc({
|
||||
|
@ -52,7 +65,7 @@ var TimelineModel = AbstractModel.extend({
|
|||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return TimelineModel;
|
||||
return TimelineModel;
|
||||
});
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
odoo.define('web_timeline.TimelineRenderer', function (require) {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
var AbstractRenderer = require('web.AbstractRenderer');
|
||||
var core = require('web.core');
|
||||
var time = require('web.time');
|
||||
var utils = require('web.utils');
|
||||
var session = require('web.session');
|
||||
var QWeb = require('web.QWeb');
|
||||
var field_utils = require('web.field_utils');
|
||||
var TimelineCanvas = require('web_timeline.TimelineCanvas');
|
||||
var AbstractRenderer = require('web.AbstractRenderer');
|
||||
var core = require('web.core');
|
||||
var time = require('web.time');
|
||||
var utils = require('web.utils');
|
||||
var session = require('web.session');
|
||||
var QWeb = require('web.QWeb');
|
||||
var field_utils = require('web.field_utils');
|
||||
var TimelineCanvas = require('web_timeline.TimelineCanvas');
|
||||
|
||||
|
||||
var _t = core._t;
|
||||
var _t = core._t;
|
||||
|
||||
var CalendarRenderer = AbstractRenderer.extend({
|
||||
var TimelineRenderer = AbstractRenderer.extend({
|
||||
template: "TimelineView",
|
||||
|
||||
events: _.extend({}, AbstractRenderer.prototype.events, {
|
||||
'click .oe_timeline_button_today': '_onTodayClicked',
|
||||
'click .oe_timeline_button_scale_day': '_onScaleDayClicked',
|
||||
'click .oe_timeline_button_scale_week': '_onScaleWeekClicked',
|
||||
'click .oe_timeline_button_scale_month': '_onScaleMonthClicked',
|
||||
'click .oe_timeline_button_scale_year': '_onScaleYearClicked',
|
||||
}),
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function (parent, state, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.modelName = params.model;
|
||||
|
@ -36,6 +45,9 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this.modelClass = this.view.model;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
var attrs = this.arch.attrs;
|
||||
|
@ -53,6 +65,9 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this._super.apply(this, self);
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered when the timeline is attached to the DOM.
|
||||
*/
|
||||
on_attach_callback: function() {
|
||||
var height = this.$el.parent().height() - this.$el.find('.oe_timeline_buttons').height();
|
||||
if (height > this.min_height) {
|
||||
|
@ -62,8 +77,10 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_render: function () {
|
||||
this.add_events();
|
||||
var self = this;
|
||||
return $.when().then(function () {
|
||||
// Prevent Double Rendering on Updates
|
||||
|
@ -74,25 +91,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
add_events: function() {
|
||||
var self = this;
|
||||
this.$(".oe_timeline_button_today").click(function() {
|
||||
self._onTodayClicked();
|
||||
});
|
||||
this.$(".oe_timeline_button_scale_day").click(function() {
|
||||
self._onScaleDayClicked();
|
||||
});
|
||||
this.$(".oe_timeline_button_scale_week").click(function() {
|
||||
self._onScaleWeekClicked();
|
||||
});
|
||||
this.$(".oe_timeline_button_scale_month").click(function() {
|
||||
self._onScaleMonthClicked();
|
||||
});
|
||||
this.$(".oe_timeline_button_scale_year").click(function() {
|
||||
self._onScaleYearClicked();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the timeline window to today (day).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onTodayClicked: function () {
|
||||
this.current_window = {
|
||||
start: new moment(),
|
||||
|
@ -104,22 +107,48 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a day.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleDayClicked: function () {
|
||||
this._scaleCurrentWindow(24);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a week.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleWeekClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 7);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a month.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleMonthClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 30);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a year.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleYearClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 365);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales the timeline window based on the current window.
|
||||
*
|
||||
* @param {Integer} factor The timespan (in hours) the window must be scaled to.
|
||||
* @private
|
||||
*/
|
||||
_scaleCurrentWindow: function (factor) {
|
||||
if (this.timeline) {
|
||||
this.current_window = this.timeline.getWindow();
|
||||
|
@ -128,7 +157,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
_computeMode: function() {
|
||||
/**
|
||||
* Computes the initial visible window.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_computeMode: function () {
|
||||
if (this.mode) {
|
||||
var start = false, end = false;
|
||||
switch (this.mode) {
|
||||
|
@ -154,6 +188,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the timeline (http://visjs.org/docs/timeline/).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
init_timeline: function () {
|
||||
var self = this;
|
||||
this._computeMode();
|
||||
|
@ -205,14 +244,24 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
draw_canvas: function() {
|
||||
/**
|
||||
* Clears and draws the canvas items.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
draw_canvas: function () {
|
||||
this.canvas.clear();
|
||||
if (this.dependency_arrow) {
|
||||
this.draw_dependencies();
|
||||
}
|
||||
},
|
||||
|
||||
draw_dependencies: function() {
|
||||
/**
|
||||
* Draw item dependencies on canvas.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
draw_dependencies: function () {
|
||||
var self = this;
|
||||
var items = this.timeline.itemSet.items;
|
||||
_.each(items, function(item) {
|
||||
|
@ -227,7 +276,17 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
draw_dependency: function(from, to, options) {
|
||||
/**
|
||||
* Draws a dependency arrow between 2 timeline items.
|
||||
*
|
||||
* @param {Object} from Start timeline item
|
||||
* @param {Object} to Destination timeline item
|
||||
* @param {Object} options
|
||||
* @param {Object} options.line_color Color of the line
|
||||
* @param {Object} options.line_width The width of the line
|
||||
* @private
|
||||
*/
|
||||
draw_dependency: function (from, to, options) {
|
||||
if (!from.displayed || !to.displayed) {
|
||||
return;
|
||||
}
|
||||
|
@ -240,6 +299,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this.canvas.draw_arrow(from.dom.box, to.dom.box, defaults.line_color, defaults.line_width);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load display_name of records.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
on_data_loaded: function (events, group_bys, adjust_window) {
|
||||
var self = this;
|
||||
var ids = _.pluck(events, "id");
|
||||
|
@ -262,6 +327,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set groups and events.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_data_loaded_2: function (events, group_bys, adjust_window) {
|
||||
var self = this;
|
||||
var data = [];
|
||||
|
@ -282,7 +352,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
// get the groups
|
||||
/**
|
||||
* Get the groups.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array}
|
||||
*/
|
||||
split_groups: function (events, group_bys) {
|
||||
if (group_bys.length === 0) {
|
||||
return events;
|
||||
|
@ -310,7 +385,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
return groups;
|
||||
},
|
||||
|
||||
/* Transform Odoo event object to timeline event object */
|
||||
/**
|
||||
* Transform Odoo event object to timeline event object.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
event_data_transform: function (evt) {
|
||||
var self = this;
|
||||
var date_start = new moment();
|
||||
|
@ -368,7 +448,14 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
return r;
|
||||
},
|
||||
|
||||
render_timeline_item: function(evt) {
|
||||
/**
|
||||
* Render timeline item template.
|
||||
*
|
||||
* @param {Object} evt Record
|
||||
* @private
|
||||
* @returns {String} Rendered template
|
||||
*/
|
||||
render_timeline_item: function (evt) {
|
||||
if(this.qweb.has_template('timeline-item')) {
|
||||
return this.qweb.render('timeline-item', {
|
||||
'record': evt,
|
||||
|
@ -381,8 +468,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a click on a group header.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_group_click: function (e) {
|
||||
// handle a click on a group header
|
||||
if (e.what === 'group-label' && e.group !== -1) {
|
||||
this._trigger(e, function() {
|
||||
// Do nothing
|
||||
|
@ -390,22 +481,47 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onUpdate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_update: function (item, callback) {
|
||||
this._trigger(item, callback, 'onUpdate');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onMove.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_move: function (item, callback) {
|
||||
this._trigger(item, callback, 'onMove');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onRemove.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_remove: function (item, callback) {
|
||||
this._trigger(item, callback, 'onRemove');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onAdd.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_add: function (item, callback) {
|
||||
this._trigger(item, callback, 'onAdd');
|
||||
},
|
||||
|
||||
/**
|
||||
* trigger_up encapsulation adds by default the rights, and the renderer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_trigger: function (item, callback, trigger) {
|
||||
this.trigger_up(trigger, {
|
||||
'item': item,
|
||||
|
@ -415,7 +531,7 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
return CalendarRenderer;
|
||||
return TimelineRenderer;
|
||||
});
|
||||
|
|
|
@ -39,11 +39,15 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
Renderer: TimelineRenderer,
|
||||
},
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
var self = this;
|
||||
this.timeline = false;
|
||||
this.arch = viewInfo.arch;
|
||||
this.arch = this.rendererParams.arch;
|
||||
var attrs = this.arch.attrs;
|
||||
this.fields = viewInfo.fields;
|
||||
this.modelName = this.controllerParams.modelName;
|
||||
|
@ -144,6 +148,9 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Order function for groups.
|
||||
*/
|
||||
group_order: function (grp1, grp2) {
|
||||
// display non grouped elements first
|
||||
if (grp1.id === -1) {
|
||||
|
@ -156,6 +163,11 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the colors attribute.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
parse_colors: function () {
|
||||
if (this.arch.attrs.colors) {
|
||||
this.colors = _(this.arch.attrs.colors.split(';')).chain().compact().map(function (color_pair) {
|
||||
|
|
Loading…
Reference in New Issue