diff --git a/web_timeline/static/src/views/timeline/timeline_controller.esm.js b/web_timeline/static/src/views/timeline/timeline_controller.esm.js index 1292e1a59bda..66bb705a91fe 100644 --- a/web_timeline/static/src/views/timeline/timeline_controller.esm.js +++ b/web_timeline/static/src/views/timeline/timeline_controller.esm.js @@ -80,6 +80,13 @@ export class TimelineController extends Component { * @returns {jQuery.Deferred} */ _onItemDoubleClick(event) { + const item_id = + typeof event.item === "string" && event.item.indexOf("_") !== -1 + ? Number(event.item.split("_")[0]) || event.item.split("_")[0] + : Number(event.item) || event.item; + + // Update event item to be just the ID + event.item = item_id; return this.openItem(event.item, false); } @@ -92,7 +99,10 @@ export class TimelineController extends Component { * @returns {Object} */ _onUpdate(item) { - const item_id = Number(item.evt.id) || item.evt.id; + const item_id = + typeof item.evt.id === "string" && item.evt.id.indexOf("_") !== -1 + ? Number(item.evt.id.split("_")[0]) || item.evt.id.split("_")[0] + : Number(item.evt.id) || item.evt.id; return this.openItem(item_id, true); } @@ -101,6 +111,12 @@ export class TimelineController extends Component { * @param {Boolean} is_editable */ openItem(item_id, is_editable) { + const _id = + typeof item_id === "string" && item_id.indexOf("_") !== -1 + ? Number(item_id.split("_")[0]) || item_id.split("_")[0] + : Number(item_id) || item_id; + item_id = _id; + if (this.open_popup_action) { const options = { resModel: this.model.model_name, @@ -243,7 +259,11 @@ export class TimelineController extends Component { context[`default_${this.date_delay}`] = diff.hours; } if (item.group > 0) { - context[`default_${this.model.last_group_bys[0]}`] = item.group; + if (this.model.fields[this.model.last_group_bys[0]].type !== "many2many") { + context[`default_${this.model.last_group_bys[0]}`] = item.group; + } else { + context[`default_${this.model.last_group_bys[0]}`] = [item.group]; + } } // Show popup this.dialogService.add( diff --git a/web_timeline/static/src/views/timeline/timeline_model.esm.js b/web_timeline/static/src/views/timeline/timeline_model.esm.js index 1c5c3ce07b65..884873364c04 100644 --- a/web_timeline/static/src/views/timeline/timeline_model.esm.js +++ b/web_timeline/static/src/views/timeline/timeline_model.esm.js @@ -64,10 +64,10 @@ export class TimelineModel extends Model { // because it is not supported by Odoo // In the module sale_timesheet_timeline, it is used // with default_group_by = task_user_ids - let field_to_order = this.params.default_group_by; - if (this.fields[field_to_order].type === "many2many") { - field_to_order = undefined; - } + const field_to_order = this.params.default_group_by; + // If (this.fields[field_to_order].type === "many2many") { + // field_to_order = undefined; + // } this.data = await this.keepLast.add( this.orm.call(this.model_name, "search_read", [], { fields: fields, @@ -87,39 +87,61 @@ export class TimelineModel extends Model { */ _event_data_transform(record) { const [date_start, date_stop] = this._get_event_dates(record); - let group = record[this.last_group_bys[0]]; - if (group && Array.isArray(group) && group.length > 0) { - group = group[0]; + const group = record[this.last_group_bys[0]]; + let groups = []; + if ( + group && + Array.isArray(group) && + group.length === 2 && + typeof group[1] === "string" + ) { + // TODO: this breaks if m2m and only 2 items + // I guess detect if its a number/id or array of ids + groups.push(group[0]); } else { - group = -1; + groups = group || []; } - let colorToApply = false; - for (const color of this.colors) { - if (evaluate(color.ast, record)) { - colorToApply = color.color; - } + if (groups.length === 0) { + groups = [-1]; } - let content = record.display_name; - if (this.recordTemplate) { - content = this._render_timeline_item(record); - } + console.log("groups", groups); + // eslint-disable-next-line prefer-const + let all_timeline_items = []; + + for (const this_group of groups) { + let colorToApply = false; + for (const color of this.colors) { + if (evaluate(color.ast, record)) { + colorToApply = color.color; + } + } - const timeline_item = { - start: date_start.toJSDate(), - content: content, - id: record.id, - order: record.order, - group: group, - evt: record, - style: `background-color: ${colorToApply};`, - }; - // Only specify range end when there actually is one. - // ➔ Instantaneous events / those with inverted dates are displayed as points. - if (date_stop && DateTime.fromISO(date_start) < DateTime.fromISO(date_stop)) { - timeline_item.end = date_stop.toJSDate(); + let content = record.display_name; + if (this.recordTemplate) { + content = this._render_timeline_item(record); + } + + const timeline_item = { + start: date_start.toJSDate(), + content: content, + id: record.id + (this_group ? `_${this_group}` : ""), + order: record.order, + group: this_group, + evt: record, + style: `background-color: ${colorToApply};`, + }; + // Only specify range end when there actually is one. + // ➔ Instantaneous events / those with inverted dates are displayed as points. + if ( + date_stop && + DateTime.fromISO(date_start) < DateTime.fromISO(date_stop) + ) { + timeline_item.end = date_stop.toJSDate(); + } + all_timeline_items.push(timeline_item); } - return timeline_item; + return all_timeline_items; } /** * Get dates from given event @@ -194,8 +216,13 @@ export class TimelineModel extends Model { * @returns {jQuery.Deferred} */ async remove_completed(event) { - await this.orm.call(this.model_name, "unlink", [[event.evt.id]]); - const unlink_index = this.data.findIndex((item) => item.id === event.evt.id); + const item_id = + typeof event.evt.id === "string" && event.evt.id.indexOf("_") !== -1 + ? Number(event.evt.id.split("_")[0]) || event.evt.id.split("_")[0] + : Number(event.evt.id) || event.evt.id; + + await this.orm.call(this.model_name, "unlink", [[item_id]]); + const unlink_index = this.data.findIndex((item) => item.id === item_id); if (unlink_index !== -1) { this.data.splice(unlink_index, 1); } diff --git a/web_timeline/static/src/views/timeline/timeline_renderer.esm.js b/web_timeline/static/src/views/timeline/timeline_renderer.esm.js index 4aaba72d0cfc..e5f7937cb138 100644 --- a/web_timeline/static/src/views/timeline/timeline_renderer.esm.js +++ b/web_timeline/static/src/views/timeline/timeline_renderer.esm.js @@ -326,12 +326,16 @@ export class TimelineRenderer extends Component { * @private */ async on_data_loaded(records, adjust_window) { - const data = []; + let data = []; // Changed to non const to allow concat + for (const record of records) { if (record[this.date_start]) { - data.push(this.model._event_data_transform(record)); + // Change here to allow multiple items per record + // Does this make the get_m2m_grouping_datas function obsolete? + data = data.concat(this.model._event_data_transform(record)); } } + const groups = await this.split_groups(records); this.timeline.setGroups(groups); this.timeline.setItems(data); @@ -425,6 +429,8 @@ export class TimelineRenderer extends Component { * @private */ on_timeline_double_click(e) { + // Fun Fact: this function never gets called. + // On_timeline_click fires twice but this never does. if (e.what === "item" && e.item !== -1) { this.props.onItemDoubleClick(e); }