Browse Source

[ADD] web_widget_date_interval

pull/1001/head
Holger Brunn 7 years ago
parent
commit
f5248f8d84
No known key found for this signature in database GPG Key ID: 1C9760FECA3AE18
  1. 1
      web_widget_date_interval/DESCRIPTION.rst
  2. 28
      web_widget_date_interval/DEVELOP.rst
  3. 1
      web_widget_date_interval/README.rst
  4. 4
      web_widget_date_interval/ROADMAP.rst
  5. 3
      web_widget_date_interval/__init__.py
  6. 23
      web_widget_date_interval/__manifest__.py
  7. 13
      web_widget_date_interval/demo/res_users.xml
  8. BIN
      web_widget_date_interval/static/description/icon.png
  9. 8
      web_widget_date_interval/static/src/css/web_widget_date_interval.css
  10. 245
      web_widget_date_interval/static/src/js/web_widget_date_interval.js
  11. 21
      web_widget_date_interval/static/src/xml/web_widget_date_interval.xml
  12. 9
      web_widget_date_interval/views/templates.xml

1
web_widget_date_interval/DESCRIPTION.rst

@ -0,0 +1 @@
This module was written to add support for a date interval widget.

28
web_widget_date_interval/DEVELOP.rst

@ -0,0 +1,28 @@
Search views
============
On search views, this addon allows developers to show a predefined range of intervals. Use ``widget="date_interval"`` and an options dictionary to configure the addon, at least a type of interval. Currently, the only supported interval type is `iso_week`: ``options="{'type': 'iso_week'}"``.
Configuration options
---------------------
type
Possible values are ``iso_week`` (shows iso week numbers)
date
The reference date from which to calculate intervals if different from `now`
lookahead
The amount of intervals after `date` to offer to the user
lookbehind
The amount of intervals before `date` to offer to the user
exclusive
If truthy, selecting one interval will unselect all already selected intervals. If not set (default), selecting multiple intervals will yield a domain for all selected intervals
cycle
If truthy, the currently selected interval will be used for setting the reference date. The effect is that you can cycle through intervals rapidly in combination with `exclusive = 1` and `dropdown = 1`
dropdown
If truthy (default), the intervals to choose are shown as a dropdown menu

1
web_widget_date_interval/README.rst

@ -0,0 +1 @@
/

4
web_widget_date_interval/ROADMAP.rst

@ -0,0 +1,4 @@
- migrate the work done in https://github.com/OCA/web/pull/575 to 10 for the form view part
- support tree and kanban views
- support much more interval types (days, months, quarters...)
- invent some textual way to fill in some interval (-1w+3w for now - one week to now + three weeks)

3
web_widget_date_interval/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

23
web_widget_date_interval/__manifest__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Date interval widget",
"version": "10.0.1.0.0",
"author": "Therp BV,Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Hidden/Dependency",
"summary": "Widget for date intervals",
"depends": [
'web',
],
"demo": [
"demo/res_users.xml",
],
"data": [
'views/templates.xml',
],
"qweb": [
'static/src/xml/web_widget_date_interval.xml',
],
}

13
web_widget_date_interval/demo/res_users.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_users_search" model="ir.ui.view">
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_search" />
<field name="arch" type="xml">
<xpath expr="." position="inside">
<field name="login_date" widget="date_interval" options="{'type': 'iso_week', 'dropdown': 0, 'exclusive': 1, 'cycle': 1}" />
<field name="create_date" widget="date_interval" options="{'type': 'iso_week', 'lookbehind': 104, 'lookahead': 1}" />
</xpath>
</field>
</record>
</odoo>

BIN
web_widget_date_interval/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

8
web_widget_date_interval/static/src/css/web_widget_date_interval.css

@ -0,0 +1,8 @@
div.o_date_interval > button > a
{
color: #4c4c4c;
}
div.o_date_interval > button > a.selected
{
font-weight: bold;
}

245
web_widget_date_interval/static/src/js/web_widget_date_interval.js

@ -0,0 +1,245 @@
//-*- coding: utf-8 -*-
//Copyright 2018 Therp BV <https://therp.nl>
//License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
odoo.define('web_widget_date_interval', function(require) {
var core = require('web.core'),
pyeval = require('web.pyeval'),
Widget = require('web.Widget'),
SearchView = require('web.SearchView');
var SearchWidgetDateInterval = Widget.extend({
template: 'SearchView.DateInterval',
events: {
'click [data-id]': 'on_click_interval',
},
_option_defaults: {
iso_week: {
lookbehind: 1,
lookahead: 10,
exclusive: false,
cycle: false,
},
},
options: {
type: 'iso_week',
dropdown: true,
},
init: function(parent, field_name, string, options) {
this.searchview = parent;
this.string = string;
this.options = _.extend(
{}, this.options, this._option_defaults[options.type] || {},
options
);
this.field = field_name;
return this._super.apply(this, arguments);
},
start: function() {
this.searchview.query.on(
'add change remove reset', this.proxy('on_searchview_change')
);
return this._super.apply(this, arguments);
},
get_intervals: function() {
var interval_function = _.str.sprintf(
'get_intervals_%s', this.options.type
);
if(!this[interval_function]) {
throw new Error('Unknown interval type given');
}
return this[interval_function]();
},
get_intervals_iso_week: function() {
var existing = this.searchview.query.findWhere({
category: this.string,
}),
reference_date = moment(
!existing || !this.options.cycle
? this.options.date
: existing.get('field').get_domain().slice(-2, -1)[0][2]
),
start_date = reference_date.clone().startOf('isoWeek')
.subtract(this.options.lookbehind, 'weeks'),
stop_date = reference_date.clone().endOf('isoWeek')
.add(this.options.lookahead, 'weeks'),
current_date = start_date.clone(),
result = [];
while(current_date.isBefore(stop_date)) {
result.push({
name: current_date.year() === moment().year()
? _.str.sprintf(core._t('%s'), current_date.isoWeek())
: _.str.sprintf(
core._t('%s / %s'), current_date.isoWeek(),
current_date.isoWeekYear()
),
_id: _.str.sprintf(
'%s-%s', this.field, current_date.format('YYYY-MM-DD')
),
start: current_date.format('YYYY-MM-DD'),
stop: current_date.add(1, 'weeks').format('YYYY-MM-DD'),
});
}
return result;
},
on_click_interval: function(e) {
var $this = jQuery(e.currentTarget);
return this._update_searchview(
$this.data('start'), $this.data('stop'), $this.text().trim()
);
},
_create_facet: function(date_start, date_stop, label) {
var self = this;
return {
field: {
get_domain: function() {
return [
'&',
[self.field, '>=', date_start],
[self.field, '<', date_stop],
];
},
// eslint-disable-next-line no-empty-function
get_context: function() {},
// eslint-disable-next-line no-empty-function
get_groupby: function() {},
},
category: this.string,
icon: 'fa-calendar',
values: [
{
label: _.str.sprintf(
'%s: %s', this.string, label
),
value: null,
},
],
_id: _.str.sprintf(
'%s-%s', self.field, date_start
),
};
},
_update_searchview: function(date_start, date_stop, label, options) {
var facet = this._create_facet(date_start, date_stop, label),
existing = this.searchview.query.findWhere({
category: this.string,
});
if(existing) {
var is_removal = existing.get('_id').includes(facet._id);
this.searchview.query.remove(existing, {silent: !is_removal});
if(!this.options.exclusive) {
// concatenate existing facet with ours
var domain = [].concat(
['|'], facet.field.get_domain(),
existing.get('field').get_domain()
);
facet._id = _.str.sprintf(
'%s %s', facet._id, existing.get('_id')
);
facet.values = facet.values.concat(existing.get('values'));
facet.field.get_domain = function() {
return domain;
};
}
if(is_removal) {
return;
}
}
this.searchview.query.add(facet, options);
},
on_searchview_change: function() {
var self = this;
this.$('[data-id]').removeClass('selected');
if(this.options.cycle) {
this.renderElement();
}
this.searchview.query.each(function(facet) {
if(facet.get('category') === self.string) {
self.$(
_.map(
facet.get('_id').split(' '), function(x) {
return _.str.sprintf('[data-id="%s"]', x);
}
).join(',')
).addClass('selected');
}
});
},
facet_for_defaults: function(values) {
var self = this,
default_value = values[_.str.sprintf(
'date_interval_%s', this.field
)],
facet = false;
if(!default_value) {
return;
}
default_value = moment(default_value);
_(this.get_intervals()).each(function(interval) {
if(
!moment(interval.start).isBefore(default_value) ||
!moment(interval.stop).isAfter(default_value)
) {
return;
}
facet = self._create_facet(
interval.start, interval.stop, interval.name
);
});
return facet;
},
visible: function() {
return false;
},
});
SearchView.include({
init: function() {
this._super.apply(this, arguments);
this.date_intervals = [];
},
start: function() {
var self = this,
deferreds = [this._super.apply(this, arguments)];
if(this.$buttons && !this.options.disable_date_interval) {
_(this.date_intervals).each(function(widget) {
deferreds.push(widget.appendTo(self.$buttons));
});
}
return jQuery.when.apply(jQuery, deferreds);
},
prepare_search_inputs: function() {
this._super.apply(this, arguments);
var self = this;
_.chain(this.fields_view.arch.children)
.filter(function(x) {
return x.tag === 'field' && x.attrs.widget === 'date_interval';
})
.each(function(x) {
var widget = new SearchWidgetDateInterval(
self, x.attrs.name, core._t(x.attrs.string) ||
self.ViewManager.search_fields_view.fields[x.attrs.name]
.string, pyeval.py_eval(x.attrs.options || '{}')
);
self.date_intervals.push(widget);
self.search_fields.push(widget);
});
},
});
return {
search_widget_date_interval: SearchWidgetDateInterval,
};
});

21
web_widget_date_interval/static/src/xml/web_widget_date_interval.xml

@ -0,0 +1,21 @@
<templates>
<div t-name="SearchView.DateInterval" class="btn-group o_date_interval o_dropdown">
<button class="o_dropdown_toggler_btn btn btn-sm dropdown-toggle" t-att-data-toggle="widget.options.dropdown and 'dropdown' or ''">
<span class="fa fa-calendar"></span>
<t t-esc="widget.string" />
<span t-if="widget.options.dropdown" class="caret" />
<t t-else="" t-att-data-date_interval="widget.field">
<t t-foreach="widget.get_intervals()" t-as="interval">
<a t-att-data-id="interval._id" t-att-data-start="interval.start" t-att-data-stop="interval.stop" href="#"><t t-esc="interval.name" /></a>
</t>
</t>
</button>
<ul t-if="widget.options.dropdown" class="dropdown-menu o_filters_menu" role="menu" t-att-data-date_interval="widget.field">
<t t-foreach="widget.get_intervals()" t-as="interval">
<li t-att-data-id="interval._id" t-att-data-start="interval.start" t-att-data-stop="interval.stop">
<a href="#"><t t-esc="interval.name" /></a>
</li>
</t>
</ul>
</div>
</templates>

9
web_widget_date_interval/views/templates.xml

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<template id="assets_backend" name="web_widget_date_interval assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/web_widget_date_interval/static/src/js/web_widget_date_interval.js"></script>
<link rel="stylesheet" href="/web_widget_date_interval/static/src/css/web_widget_date_interval.css"/>
</xpath>
</template>
</odoo>
Loading…
Cancel
Save